Linux内核mmc_sdio_sd卡子系统驱动架构

Linux内核mmc_sdio_sd卡子系统驱动架构
Linux内核mmc_sdio_sd卡子系统驱动架构

MMC/SD Memory/SDIO卡mmc子系统驱动架构工作报告

2014-3-11深圳

目标:

分析整理(MMC/SD Memory/SDIO卡)mmc子系统驱动架构;

本文要点:

1、mmc子系统后端初始化相关的数据结构;

2、mmc子系统初始化驱动架构;

3、mmc请求处理;

4、基本的sd协议规范;

SDIO/SD Memory/MMC卡的区别:

●SDIO card is to provide high-speed data I/O with low power consumption for mobile

electronic devices.

●The SDIO (Secure Digital I/O) card is based on and compatible with the SD memory card. This

compatibility includes mechanical, electrical, power, signaling and software.

●SD Memory Card is a memory card that is specifically designed to meet the security,

capacity, performance, and environment requirements inherent in newly emerging audio and video consumer electronic devices.

The SD Memory Card will include a content protection mechanism.The SD Memory Card security system uses mutual authentication and a "new cipher algorithm" to protect against illegal usage of the card content.

A Non-secure access to the user's own content is also available.

●The MMC/eMMC is an universal low cost data storage and communication media. It is

designed to cover a wide area of applications as smart phones, cameras, organizers, PDAs, digital recorders, MP3 players, pagers, electronic toys, etc. Targeted features are high mobility and high performance at a low cost price.

These features include low power consumption and high data throughput at the memory card interface.

●SD Memory卡是从MMC卡发展过来的,它更注重内容保护,SD Memory卡在外形上同

MMC卡保持一致,且兼容MMC卡兼容规范.

所以就发展轨道来看,是MMC卡==>SD Memory卡==>SDIO卡,依次兼容,保持一致

硬件图:(以SD 总线为例)

注意:在默认速度下,SD 总线支持单master(application)与多slaves(cards)连接;而在高速和UHS-I(极高速)下,SD 总线只支持单master 与单slave 连接;(经分析:当前linux 版本代码针对的是单master 与单slave 连接模式)

D0-D3, CMD

VSS

VDD CLK

D0-D3, CMD

CLK VDD VSS

CLK VDD VSS D0-D3(A) CMD(A)

D0-D3(B) CMD(B)

SD Memory

Card(B)

SD Memory Card(A)

Host

数据结构

1.初始化:

mmc_card {} .dev ... ... .cid .csd *host ... ...

mmc_host {} *card

.class_dev *ops .disable .detect ... ... .ios *bus_ops

mmc_csd{} .max_dtr .cmdclass .capacity ... ...

mmc_cid{} .manfid ... ...

mmc_host_ops{} *request *set_ios *get_ro ... ...

sdhci_host {} *mmc ... ...

(前端)

gendisk {} *driverfs_dev .major .first_minor ... ...

mmc_bus_type {} *match *probe ... ...

device{} *bus ... ...

mmc_bus_match

mmc_bus_probe device_driver{} *bus ... ...

mmc_driver {} .drv .probe

... ...

mmc_blk_data {} .queue *disk ... ... mmc_queue{}

*card ... ...

(前端)

mmc_ios{} clock vdd bus_mode ... ...

delayed_work{} ... ...

mmc_sd_ops{} mmc_ops{} mmc_sdio_ops{}

其中:*ops :表示对控制器的操作,由前端控制器驱动中设定;

*bus_ops :表示对总线上卡的操作,不同类型总线对应的卡有不同的操作,其中mmc_sd_ops{}, 在mmc_sd_attach_bus_ops()

函数中设置; mmc_sdio_ops{} 在mmc_attach_bus()函数中设置; mmc_ops{} 在mmc_attach_bus_ops 函数中设置;

2.请求处理:

mmc_command {} *data *mrq opcode arg resp[4] flags retries ... ...

mmc_blk_data {}

queue ... ...

mmc_queue {} *data

*queue *thread *issue_fn *sg *req

mmc_data {} *mrq *stop *sg blksz blocks flags ... ...

request_queue {} queue_head *queuedata request {}

queuelist

mmc_blk_request {} mrq data cmd stop

mmc_request {} *cmd *data *stop *done ... ...

task_struct{} ... ...

scatterlist{} ... ...

(块层构建的请求)

request {} queuelist 注1

(当前取出的请求)

(mmc 构建的请求)

(mmc 构建的命令)

注1:不论是块层下发的读写请求,还是源自mmc 核心层要发出的命令,都使用mmc_request {}进行发送 (原因:保存了所需资源的指针); 注2:有两种命令来源,一种是mmc 核心需要发的命令,一种是块层下发的请求也需要由mmc 为其生成命令。两个都是在mmc 子系统中构建;

注3:mmc 核心需要发的命令,以初始化为目的,不传输数据;由块层下发的请求所生成的命令,以数据读写为目的,要用到mmc_data {};

注2 注3 mmc_request()

数据结构成员:

struct mmc_card {

struct mmc_host *host; /* 隶属的MMC控制器*/

struct device dev; /* 当前设备*/

unsigned int rca; /* 卡地址*/

unsigned int type; /* 卡的类型: 0=MMC 1=SD 2=SDIO*/

unsigned int state; /* 卡状态*/

u32 raw_cid[4]; /* 卡cid,未解码*/

u32 raw_csd[4]; /* 卡csd,未解码*/

u32 raw_scr[2]; /* 卡scr,未解码*/

... ...;

struct mmc_cid cid; /* 卡身份,已解码*/

struct mmc_csd csd; /* 卡的特殊数据,已解码*/

struct sd_scr scr; /* 额外的SD数据,已解码*/

... ...;

};

mmc_card{}代表了一张MMC卡

struct mmc_cid {

unsigned int manfid; /* 制造商id*/

char prod_name[8]; /* 产品名*/

unsigned int serial; /* 产品序列号*/

unsigned short oemid; /* 产品OID*/

... ...

};

struct mmc_csd {

unsigned char mmca_vsn; /*卡版本号*/

unsigned short cmdclass; /* card支持的命令子集*/

unsigned short tacc_clks; /*用于计算读命令的末位和所读数据的首位间的最大时值Nac*/

unsigned int tacc_ns; /*用于计算读命令的末位和所读数据的首位间的最大时值Nac*/

unsigned int max_dtr; /*最大传输速率*/

unsigned int read_blkbits; /*最大的读数据块长度*/

unsigned int write_blkbits; /*最大的写数据块长度*/

unsigned int capacity; /*卡的存储容量*/

unsigned int read_partial:1, /*读小块尺寸数据(最小1byte)是否允许,SD卡恒为1*/

... ...

};

mmc_cid{},mmc_csd{}是MMC卡的CID寄存器与CSD寄存器相关的结构

struct sd_scr {

unsigned char sda_vsn; /*physical layer specification版本号*/

unsigned char bus_widths; /*数据线的宽度*/

};

struct mmc_blk_data {

struct gendisk *disk; /*通用磁盘*/

struct mmc_queue queue; /*磁盘设备的请求队列数据结构*/

unsigned int read_only; /*只读标志*/

... ...;

};

struct gendisk {

int major; /* 磁盘的主设备号*/

int first_minor; /* 磁盘的第一个次设备号*/

int minors; /* 次设备号数目,即分区个数, 至少为1*/ struct disk_part_tbl *part_tbl; /* 指向磁盘分区表*/

struct hd_struct part0; /* 第一个分区:0分区*/

struct block_device_operations *fops; /* 磁盘块设备操作方法*/

struct request_queue *queue; /* 指向磁盘设备的请求队列*/

void *private_data; /* 磁盘的私有数据*/

int flags; /* 磁盘类别标志*/

char disk_name[DISK_NAME_LEN]; /* 磁盘名*/

struct device *driverfs_dev; /* 对应的当前设备*/

... ...;

};

struct mmc_queue {

struct mmc_card *card; /* 对应的卡设备*/

struct task_struct *thread; /* 请求队列关联的处理线程*/

struct request *req; /* 取出的一个请求*/

int (*issue_fn)(struct mmc_queue *, struct request *); /* 发送请求的函数*/

void *data; /*这里用来保存mmc_blk_data{}的指针*/ struct request_queue *queue; /* 磁盘设备的请求队列*/

struct scatterlist *sg; /* 磁盘设备的散列表*/

... ...;

};

struct mmc_host {

struct device class_dev; /* 当前mmc控制器设备*/

const struct mmc_host_ops *ops; /* 控制器的操作方法*/

u32 ocr_avail; /* 控制器支持的运行电压*/

unsigned long caps; /* 控制器支持的传输能力*/

unsigned int max_seg_size; /* 最大的数据段尺寸(bytes)*/

unsigned short max_hw_segs; /* 一条请求的硬件扇区数上限(通常以512b为单位)*/

unsigned short max_phys_segs;

unsigned int max_req_size; /* 一条请求的最大字节数(bytes)*/

unsigned int max_blk_size; /* 一个mmc数据块的最大尺寸(bytes)*/

unsigned int max_blk_count; /* 一条请求中的数据块数目上限*/

struct mmc_ios ios; /* 当前的总线设置*/

struct delayed_work disable; /* 延迟的事物,用于关闭控制器*/

struct mmc_card *card; /* 与控制器关联的卡*/

unsigned int claimed:1; /* 控制器占用声明*/

unsigned int bus_dead:1; /* 控制器总线被释放*/

wait_queue_head_t wq; /* 声明占用控制器的等待队列头结点*/

struct task_struct *claimer; /* 占用控制器的进/线程*/

int claim_cnt; /* 计数*/

struct delayed_work detect; /* 延迟的事务,用于探测*/

const struct mmc_bus_ops *bus_ops; /* 当前绑定的总线操作*/

unsigned int bus_refs; /* 引用计数*/

... ...;

unsigned long private[0] ;

};

mmc_host{}代表一个MMC控制器

struct mmc_host_ops {

int (*enable)(struct mmc_host *host); /* 使控制器进入省电状态*/

int (*disable)(struct mmc_host *host, int lazy); /* 使控制器退出省电状态*/

void(*request)(struct mmc_host *host, struct mmc_request *req); /* 控制器处理请求的接口*/

void(*set_ios)(struct mmc_host *host, struct mmc_ios *ios); /* 控制器总线设置的接口*/

... ...;

};

struct mmc_bus_ops {

void (*remove)(struct mmc_host *); /* 释放卡*/

void (*detect)(struct mmc_host *); /* 查询卡的状态*/

int (*suspend)(struct mmc_host *); /* 使卡退出传输状态*/

int (*resume)(struct mmc_host *); /* 使已存在的卡恢复状态*/

void (*power_restore)(struct mmc_host *); /* 使已存在的卡恢复状态(不使用高速模式)*/

};

struct mmc_ios {

unsigned int clock; /* 时钟*/

unsigned short vdd; /* 电压*/

unsigned char bus_mode; /* 命令输出采用的模式*/ unsigned char chip_select; /* SPI芯片片选*/

... ...;

unsigned char bus_width; /* 总线宽度(1,4,8)*/

unsigned char timing; /*时序模式*/

};

mmc_ios{}存储MMC卡的状态信息

struct bus_type mmc_bus_type = {

.name = "mmc", /* bus type 名*/

.match = mmc_bus_match, /* bus type 匹配函数*/

.probe = mmc_bus_probe, /* bus type 匹配处理*/

... ...,

};

struct mmc_driver {

struct device_driver drv; /* 当前驱动*/

int (*probe)(struct mmc_card *); /* 驱动的匹配处理函数*/

... ...

};

struct mmc_command {

u32 opcode; /* 命令操作码*/

u32 arg; /* 命令参数*/

u32 resp[4]; /* 反馈信息*/

unsigned int flags; /* 期待的反馈类型*/

unsigned int retries; /* 重发次数*/

unsigned int error; /* 命令出错*/

struct mmc_data *data; /* 命令关联的数据段*/

struct mmc_request *mrq; /* 关联的请求*/

};

struct mmc_data {

unsigned int timeout_ns; /* 超时时间ns (max 80ms) */ unsigned int timeout_clks; /* 超时时钟数(in clocks) */ unsigned int blksz; /* 块尺寸*/

unsigned int blocks; /* 块数目*/

unsigned int error; /* 数据错误*/

unsigned int flags; /* 读写标志*/

unsigned int bytes_xfered; /* 待传输的字节数*/

struct mmc_command *stop; /* stop命令*/

struct mmc_request *mrq; /* 关联的请求*/

unsigned int sg_len; /* 散列表的尺寸*/

struct scatterlist *sg; /* 散列表指针*/

};

struct mmc_blk_request {

struct mmc_request mrq; /* 当前请求*/

struct mmc_command cmd; /* 请求对应的命令*/

struct mmc_command stop; /* 请求的stop命令*/

struct mmc_data data; /* 请求对应的数据*/

};

struct mmc_request {

struct mmc_command *cmd; /* 请求对应的命令的指针*/

struct mmc_data *data; /* 请求对应的数据的指针*/

struct mmc_command *stop; /* 请求对应的stop命令的指针*/ void *done_data; /* completion参数*/

void (*done)(struct mmc_request *); /* completion函数*/

};

第一篇:初始化架构:

mmc 子系统就初始化而言,主要有三条线、五个环节

说明:

1、最首先进行的是mmc 子系统核心初始化,下面的初始化整体框架图只是为了排版的需要位置滞后;

2、第二、三条线的先后顺序无需固定;

初始化整体框架:

第一条线 1. mmc 子系统核心初始化

subsys_initcall 3. mmc 控制器设备注册

4.探测、分配和注册card 设备

第二条线 2. mmc 控制器驱动平台注册

module_init 5.磁盘设备驱动注册

第三条线 module_init

11/44

//work : &host->detect

mmc_sd_init_card

mmc_init

workqueue = create_singlethread_workqueue("kmmcd") sdhci_probe_slot

mmc_detect_change

mmc_schedule_delayed_work(&host->detect , ...) queue_delayed_work (workqueue , work , ...) mmc_rescan ... ....(详见下文)

mmc_sd_attach_bus_ops

2. mmc 控制器

平台驱动注册 3.mmc 控制器设备注册 1. mmc 子系统

核心初始化 5.磁盘设备驱动注册

struct mmc_driver mmc_driver = {

.drv = {

.name = "mmcblk",

},

.probe

= mmc_blk_probe ,

}

add_disk

module_init sdhci_drv_init platform_driver_registe struct bus_type mmc_bus_type = { .match= mmc_bus_match,

... ...,

.probe = mmc_bus_probe , }

4.2分配和注册 card 设备

mmc_attach_sd

module_init mmc_blk_init

mmc_register_driver (&mmc_driver ) register_blkdev driver_register

device_add

mmc_add_card

mmc_alloc_host

mmc_add_host device_add mmc_start_host

queue_delayed_work

sdhci_probe 平台驱动机制流程

(见下行)

INIT_DELAYED_WORK(&host->detect , mmc_rescan ) 驱动注册并挂入总线

设备注册并挂入总线

subsys_initcall bus_register(&mmc_bus_type )

mmc_register_bus (见下行)

4.1探测

(以SD 为例)

12/44

一、mmc 子系统核心初始化:

struct workqueue_struct *workqueue ;

struct wake_lock mmc_delayed_work_wake_lock ; struct bus_type mmc_bus_type = { .name = "mmc",

.match = mmc_bus_match, .probe

= mmc_bus_probe,

... ...,

};

struct class mmc_host_class = { .name

= "mmc_host",

.dev_release = mmc_host_classdev_release,

};

struct bus_type sdio_bus_type = { .name = "sdio", .dev_attrs = sdio_dev_attrs, .match = sdio_bus_match,

.uevent

= sdio_bus_uevent,

mmc_init

总线类型mmc_bus_type 注册(SD/mmc )

workqueue= create_singlethread_workqueue("kmmcd") 为控制器设备注册一个类mmc_host_class

总线类型sdio_bus_type 注册(SDIO )

subsys_initcall

13/44

.probe = sdio_bus_probe, .remove

= sdio_bus_remove,

};

static int __init mmc_init (void) { wake_lock_init(&mmc_delayed_work_wake_lock , WAKE_LOCK_SUSPEND, "mmc_delayed_work"); workqueue = create_singlethread_workqueue("kmmcd"); mmc_register_bus (); mmc_register_host_class(); sdio_register_bus();

}

int mmc_register_bus (void) { return bus_register (&mmc_bus_type );

}

int mmc_register_host_class(void) {

return class_register(&mmc_host_class ); }

int sdio_register_bus(void) { return bus_register(&sdio_bus_type); }

subsys_initcall (mmc_init )===> android 休眠锁:在android 机制中,只有有进程拿到wake_lock 锁且未释放,则系统不能进入休眠. 在之后的mmc_rescan()中完成mmc 卡的探测,注册之后,即释放该锁

14/44

二、mmc 控制器驱动与设备注册:

架构设计思想:

说明:sdhci_host{}是对mmc_host{} 的外封装;

函数调用图:

module_init (sdhci_drv_init)

sdhci_driver 平台驱动注册

sdhci_probe_slot() mmc_host{}

mmc->ops = &sdhci_ops

host->class_dev.class = &mmc_host_class ;

INIT_DELAYED_WORK(&host->detect, mmc_rescan ) mmc_detect_change (host, 0)

mmc_power_off(host) 探测设备的具体处理例程 断电的处理例程 启动时第一次探测设备

命令发送超时的相关处理

分配数据结构内存

mmc_host{}、sdhci_host{}内存分配

INIT_WORK(&host->finish_wq, sdhci_finish_worker )

setup_timer(&host->timer, sdhci_timeout_timer,... ... )

sdhci_host{}

命令发送完成的相关处理

request_irq(host->irq,sdhci_irq , ... ... );

命令数据收发中断的处理

15/44

代码骨架:

(属前端驱动部分,以mx_sdhci.c 为例) struct mmc_host_ops sdhci_ops = {

.request = sdhci_request, //展开见后文 .set_ios = sdhci_set_ios,//展开见后文 ... ..., };

struct platform_driver sdhci_driver = { .driver = { .name = "mxsdhci", }, .probe = sdhci_probe ,

... ..., };

//前端:

int __init sdhci_drv_init (void)

request_irq(host->detect_irq, sdhci_cd_irq ) sdhci_probe_slot device_add

mmc_power_off

mmc_add_host

mmc_start_host

mmc_detect_change

module_init (sdhci_drv_init)===> mmc_alloc_host

INIT_DELAYED_WORK(&host->detect, mmc_rescan , …)

device_initialize

16/44

{ return platform_driver_register (&sdhci_driver );

}

static int sdhci_probe (struct platform_device *pdev)

{ ... ...;

sdhci_probe_slot (pdev, ...); }

static int __devinit sdhci_probe_slot (struct platform_device *pdev, ...) { struct mmc_host *mmc;

struct sdhci_host *host; ... ...; mmc = mmc_alloc_host (sizeof(struct sdhci_host), &pdev->dev);

host = mmc_priv(mmc);//return (void *)host->private ... ...;

host->irq = platform_get_irq(pdev, 0);

host->detect_irq = platform_get_irq(pdev, 1);

mmc->index =pdev->id;

dev_set_name(&mmc->class_dev, "mmc%d", mmc->index); //如mmc0 mmc->ops = &sdhci_ops ; ……

//Q:在什么地方执行这个tasklet //A:在sdhci_irq 中断处理程序中

tasklet_init(&host->card_tasklet, sdhci_tasklet_card, (unsigned long)host); host->workqueue = create_workqueue("esdhc_wq"); INIT_WORK(&host->cd_wq, esdhc_cd_callback);

INIT_WORK(&host->finish_wq, sdhci_finish_worker); //sdhci_finish_worker 见第二篇

setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); //sdhci_timeout_timer 见第二篇 setup_timer(&host->cd_timer, sdhci_cd_timer, (unsigned long)host); ... ...;

struct platform_device mxcsdhc1_device = { .name = "mxsdhci", .id = 0,

... ...,

} 平台驱动机制流程

匹配资源调用probe 传入设备资源

17/44

//这个中断是通过GPIO 引脚检测SD 卡拔插的中断

ret = request_irq(host->detect_irq, sdhci_cd_irq, 0,pdev->name, host); //这个中断是用来检测 SD write/read CMD 错误的中断

ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, pdev->name, host); mmc_add_host (mmc);

}

struct mmc_host *mmc_alloc_host (int extra, struct device *dev) { struct mmc_host *host ;

host = kzalloc (sizeof(struct mmc_host) + extra, GFP_KERNEL);

host->class_dev.class = &mmc_host_class ;

... ...; INIT_DELAYED_WORK(&host->detect , mmc_rescan ); device_initialize (&host->class_dev ); return host; }

int mmc_add_host (struct mmc_host *host) { device_add (&host->class_dev ); mmc_start_host (host); }

void mmc_start_host (struct mmc_host *host) { mmc_power_off (host);

//这里以控制器启动为契机,引出“三、card 设备的探测、分配和注册”(见后) mmc_detect_change (host, 0); }

static void mmc_power_off (struct mmc_host *host) { host->ios.clock = 0;

名为class_dev ,实为mmc/sd memory/sdio 卡的

mmc_host{}设备 mmc_rescan 接口,主要承担card 的探测、分配和注册。展开见后文:

由mmc_detect_change/mmc_start_host 发起调用

host->ios.vdd = 0;

host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;

host->ios.chip_select = MMC_CS_DONTCARE;

host->ios.power_mode = MMC_POWER_OFF;

host->ios.bus_width = MMC_BUS_WIDTH_1;

host->ios.timing = MMC_TIMING_LEGACY;

mmc_set_ios(host);

}

static inline void mmc_set_ios(struct mmc_host *host)

{

struct mmc_ios *ios = &host->ios;

host->ops->set_ios(host, ios); //进入控制器前端驱动完成具体设置,

//.set_ios = sdhci_set_ios

}

void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

{

//具体的前端寄存器操作。将设置的数据写入imx51 esdhc控制器使其生效}

mmc_set_timing mmc_power_up mmc_select_voltage mmc_set_bus_width mmc_set_bus_mode

mmc_set_clock

mmc_set_chip_select mmc_power_down

mmc_set_ios

host->ops->set_ios sdhci_set_ios

struct mmc_host_ops sdhci_ops = {

.........,

.set_ios = sdhci_set_ios, }

前端驱动中:

mmc_host{}->ops = &sdhci_ops;

18/44

三、card设备的探测、分配和注册:

1) 探测card设备:

探测时机:

1、mmc控制器启动时;

函数链:

module_init==>sdhci_drv_init==>platform_driver_register==>... ...(控制器平台驱动注册,获取设备资源)

==>sdhci_probe==>sdhci_probe_slot==>mmc_add_host==>mmc_start_host==>mmc_detect_change ;

2、mmc控制器启动后,某时刻插入或拔出card设备时,这里涉及到mmc控制器前端驱动的两种处理:

1)、采用中断机制,经过简单的中断转调后,将调用到这里的mmc_detect_change()函数;

准备工作:(以mx_sdhci.c为例)

request_irq(host->detect_irq, sdhci_cd_irq, 0, pdev->name, host);

INIT_WORK(&host->cd_wq, esdhc_cd_callback);

函数链:

插卡/拔卡触发中断==>sdhci_cd_irq==>schedule_work(&host->cd_wq)==>esdhc_cd_callback==>

mmc_detect_change;

使用poll方式轮询探测,也将调用mmc_schedule_delayed_work()函数。

准备工作:前端开启MMC_CAP_NEEDS_POLL

如struct mmc_host host_for_instance;

host_for_instance.cap |= MMC_CAP_NEEDS_POLL;

(说明:mx_sdhci.c并没有使用轮询方式,这里对MMC_CAP_NEEDS_POLL的设置仅仅是个示意)

函数链:见后面rescan()函数内部即可;

3、mmc控制器从suspend转为resume时(电源管理)

说明:这里为电源管理服务考虑,在进入suspend之前如果已有卡处于插入态,当控制器resume返回时,需要重新进行确认并进行恢复;

准备工作:(仍以mx_sdhci.c为例)

struct platform_driver sdhci_driver = {

... ...,

.resume = sdhci_resume,

};

platform_driver_register(&sdhci_driver);

函数链:

19/44

电源管理模块==>sdhci_resume(控制器先)==>mmc_resume_host==>

host->bus_ops->resume (card后)

mmc_detect_change (待控制器和card设备依次resume后,才执行确认性探测工作) (本部分为了前后连贯突出主干,承接上文,以时机1来引起探测过程,并解析。)

20/44

Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型IO和休眠]

Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻 塞型I/O和休眠] Linux设备驱动程序学习(5) -高级字符驱动程序操作[(2)阻塞型I/O和休眠]这一部分主要讨论:如果驱动程序无法立即满足请求,该如何响应?(65865346) 一、休眠 进程被置为休眠,意味着它被标识为处于一个特殊的状态并且从调度器的运行队列中移走。这个进程将不被在任何CPU 上调度,即将不会运行。直到发生某 些事情改变了那个状态。安全地进入休眠的两条规则: (1)永远不要在原子上下文中进入休眠,即当驱动在持有一个自旋锁、seqlock或者RCU 锁时不能睡眠;关闭中断也不能睡眠。持有一个信号量时休眠是 合法的,但你应当仔细查看代码:如果代码在持有一个信号量时睡眠,任何其他的等待这个信号量的线程也会休眠。因此发生在持有信号量时的休眠必须短暂, 而且决不能阻塞那个将最终唤醒你的进程。 (2)当进程被唤醒,它并不知道休眠了多长时间以及休眠时发生什么;也不知道是否另有进程也在休眠等待同一事件,且那个进程可能在它之前醒来并获取了 所等待的资源。所以不能对唤醒后的系统状态做任何的假设,并必须重新检查等待条件来确保正确的响应。 除非确信其他进程会在其他地方唤醒休眠的进程,否则也不能睡眠。使进程可被找到意味着:需要维护一个称为等待队列的数据结构。它是一个进程链表,其中饱含了等待某个特定事件的所有进程。在Linux 中,一个等待队列由一个wait_queue_head_t 结构体来管理,其定义在中。 wait_queue_head_t 类型的数据结构非常简单: 它包含一个自旋锁和一个链表。这个链表是一个等待队列入口,它被声明做wait_queue_t。wait_queue_head_t包含关于睡眠进程的信息和它想怎样被唤

设备驱动加到Linux内核中

7.2.3 设备驱动加到Linux内核中 设备驱动程序编写完后将该驱动程序加到内核中。这需要修改Linux 的源代码,然后重新编译内核。 ①将设备驱动程序文件(比如mydriver.c)复制到/Linux/drivers/char目录下。该目录保存了Linux下字符设备的设备驱动程序。修改该目录下mem.c 文件,在int chr_dev_init()函数中增加如下代码: #ifdef CONFIG_MYDRIVER device_init(); #endif 其中CONFIG_MYDRIVER是在配置Linux内核时赋值。 ②在/linux/drivers/char目录下Makefile中增加如下代码: ifeq ($(CONFIG_MYDRIVER),y) L_OBJ + = mydriver.o endif 如果在配置Linux内核时选择了支持新定义的设备,则在编译内核时会编译mydriver.c生成mydriver.o文件。 ③修改/linux/drivers/char目录下config.in文件,在 comment Character devices 语句下面加上 bool suppot for mydriver CONFIG_MYDRIVER 这样,若编译内核,运行make config,make menuconfig或make xconfig,那么在配置字符设备时就会有选项: Support for mydriver 当选中这个设备时,设备驱动就加到了内核中了。 重新编译内核,在shell中将当前目录cd 到Linux目录下,然后执行以下代码: # make menuconfig # make dep # make 在配置选项时要注意选择支持用户添加的设备。这样得到的内核就包含用户的设备驱动程序。 Linux通过设备文件来提供应用程序和设备驱动的接口,应用程序通过标准的文件操作函数来打开、关闭、读取和控制设备。查看Linux文件系统下的/proc/devices,可以看到当前的设备信息。如果设备驱动程序已被成功加进,这里应该由该设备对应的项。/proc/interrupts纪录了当时中断情况,可以用来查看中断申请是否正常;对于DMA和I/O口的使用,在/proc下都有相应的文件进行记录;还可以在设备驱动程序中申请在/proc 文件系统下创建一个文件,该文件用来存放设备相关信息。这样通过查看该文件就可以了解设备的使用情况。总之,/proc文件系统为用户提供了查

linux驱动开发的经典书籍

linux驱动开发的经典书籍 结构、操作系统、体系结构、编译原理、计算机网络你全修过 我想大概可以分为4个阶段,水平从低到高 从安装使用=>linux常用命令=>linux系统编程=>内核开发阅读内核源码 其中学习linux常用命令时就要学会自己编译内核,优化系统,调整参数 安装和常用命令书太多了,找本稍微详细点的就ok,其间需要学会正则表达式 系统编程推荐《高级unix环境编程》,黑话叫APUE 还有《unix网络编程》 这时候大概还需要看资料理解elf文件格式,连接器和加载器,cmu的一本教材中文名为《深入理解计算机系统》比较好 内核开发阅读内核源码阶段,从写驱动入手逐渐深入linux内核开发 参考书如下《linux device drivers》,黑话叫ldd 《linux kernel development》,黑话叫lkd 《understading the linux kernel》,黑话叫utlk 《linux源码情景分析》 这四本书为搞内核的必读书籍 最后,第三阶段和第四阶段最重动手,空言无益,光看书也不罩,不动手那些东西理解不了 学习linux/unix编程方法的建议 建议学习路径: 首先先学学编辑器,vim, emacs什么的都行。 然后学make file文件,只要知道一点就行,这样就可以准备编程序了。 然后看看《C程序设计语言》K&R,这样呢,基本上就可以进行一般的编程了,顺便找本数据结构的书来看。 如果想学习UNIX/LINUX的编程,《APUE》绝对经典的教材,加深一下功底,学习《UNP》的第二卷。这样基本上系统方面的就可以掌握了。 然后再看Douglus E. Comer的《用TCP/IP进行网际互连》第一卷,学习一下网络的知识,再看《UNP》的第一卷,不仅学习网络编程,而且对系统编程的一些常用的技巧就很熟悉了,如果继续网络编程,建议看《TCP/IP进行网际互连》的第三卷,里面有很多关于应用

am335x_linux-3.14.43内核移植笔记

本文主要描述在EVB335X-II以Device Tree的方式移植新TI官网AM335X系列最新的linux-3.14.43版本内核以及移植Debian文件系统的过程及遇到的一些问题。整个Device Tree牵涉面比较广,即增加了新的用于描述设备硬件信息的文本格式(即.dts文件),又增加 了编译这一文本的工具,同时Bootloader也需要支持将编译后的Device Tree传递给Linux 内核。以下是修改步骤: 一、修改uboot,支持Device Tree EVB335X-II在linux-3.2版本内核移植的时候已经有uboot,因此只需在该uboot上增加Device Tree支持即可,以下是修改步骤: 1、修改include/configs/com335x.h文件,增加支持DT的宏定义: /* Flattened Device Tree */ #define CONFIG_OF_LIBFDT 2、修改uboot启动参数,增加dtb文件的加载和启动(由于目前只是移植EMMC版本的EVB335X-II,因此只需修改EMMC的启动参数即可,大概在405行),修改如下: #elif defined(CONFIG_EMMC_BOOT) #define CONFIG_BOOTCOMMAND \ "run mmcboot;" #define CONFIG_EXTRA_ENV_SETTINGS \ "lcdtype=AUO_AT070TN94\0" \ "console=ttyO0,115200n8\0" \ "mmcroot=/dev/mmcblk0p2 rw\0" \ "mmcrootfstype=ext4 rootwait\0" \ "mmcargs=setenv bootargs console=${console} noinitrd root=${mmcroot} rootfstype=${mmcrootfstype} lcdtype=${lcdtype} consoleblank=0\0" \ "mmcdev=" MMCDEV "\0" \ "loadaddr=0x81000000\0" \ "dtbfile=evb335x-ii-emmc.dtb\0" \ "bootenv=uEnv.txt\0" \ "bootpart=" BOOTPART "\0" \ "loadbootenv=load mmc ${mmcdev} ${loadaddr} ${bootenv}\0" \ "importbootenv=echo Importing environment from mmc ...; " \ "env import -t $loadaddr ${filesize}\0" \ "loadaddr-dtb=0x82000000\0" \ "loadimage=load mmc ${bootpart} ${loadaddr} uImage\0" \ "loaddtb=load mmc ${bootpart} ${loadaddr-dtb} ${dtbfile}\0" \ "mmcboot=mmc dev ${mmcdev}; " \ "if mmc rescan; then " \ "echo SD/MMC found on device ${mmcdev};" \ "if run loadbootenv; then " \ "echo Loaded environment from ${bootenv};" \ "run importbootenv;" \ "fi;" \ "run mmcargs;" \

Linux内核驱动加载顺序

Linux内核驱动加载顺序 【问题】 背光驱动初始化先于LCD驱动初始化,导致LCD驱动初始化时出现闪屏的现象。 【解决过程】 1 mach-xxx.c中platform devices列表如下 /* platform devices */ static struct platform_device *athena_evt_platform_devices[] __initdata = { //&xxx_led_device, &xxx_rtc_device, &xxx_uart0_device, &xxx_uart1_device, &xxx_uart2_device, &xxx_uart3_device, &xxx_nand_device, &xxx_i2c_device, &xxx_lcd_device, &xxxpwm_backlight_device, ... }; LCD(xxx_lcd_device)设备先于PWM(xxxpwm_backlight_device)设备。 可见驱动的初始化顺序并不是和这个表定义的顺序始终保持一致的。(记得PM操作 - resume/suspend 的顺序 是和这个表的顺序保持一致的) 2 怀疑和编译顺序有关 Z:\kernel\drivers\video\Makefile:背光驱动(backlight/)的编译限于LCD驱动(xxxfb.o)的编译 obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ display/ ... obj-$(CONFIG_FB_xxx) += xxxfb.o ak_logo.o obj-$(CONFIG_FB_AK88) += ak88-fb/ 这样编译生成的System.map中的顺序为: 906 c001f540 t __initcall_pwm_backlight_init6 907 c001f544 t __initcall_display_class_init6 908 c001f548 t __initcall_xxxfb_init6 Makefile更改为: obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += display/

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

Linux设备驱动模型之platform总线深入浅出

Linux设备驱动模型之platform总线深入浅出 在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 对于依附在USB、PCI、I2C、SPI等物理总线来这些都不是问题。但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。基于这一背景,Linux发明了一种总线,称为platform。相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。 platform总线相关代码:driver\base\platform.c 文件相关结构体定义:include\linux\platform_device.h 文件中 platform总线管理下最重要的两个结构体是platform_device和platform_driver 分别表示设备和驱动在Linux中的定义如下一:platform_driver //include\linux\platform_device.h struct platform_driver { int (*probe)(struct platform_device *); //探测函数,在注册平台设备时被调用int (*remove)(struct platform_device *); //删除函数,在注销平台设备时被调用void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); //挂起函数,在关机被调用int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *);//恢复函数,在开机时被调用struct device_driver driver;//设备驱动结构}; 1 2 3 4 5 6 7 8

linux笔记

20150526 echo adfkjeroiu > /var/www/html/index.html service httpd restart ifconfig XXX.XXX.XXX.XXX elinks XXX.XXX.XXX.XXX web地址栏:XXX.XXX.XXX.XXX 20150527 方法一:Setup 设置IP 方法二:vim /etc/sysconfig/network-XXX/ifcfg-eth0 onboot=no改onboot=yes service network restart 虚拟机中安装2个linux,有时2个linux无法连接网络,即使是DHCP自动获取,也不可以;解决办法:打开其中一个linux虚拟机,单机“右下角-小电脑图标” —“设置”—“桥接模式(B);直接连接屋里网络” ,确定即可; 20150528 more /etc/issue 查看当前linux是centos还是redhat; man 命令查看当前命令的使用方法及参数 table 当一个命令不记得全部字母,可以双击table补齐; ctrl +c 终止当前程序 ctrl +l 清屏 20150529 ls -l查看命令;(-l显示更多属性) ls –a 查看隐藏文件; cp -r /etc/aaa /home/bbb复制/etc下的aaa 到/home下,并且改名bbb; (-r是整个文件夹的意思,如果,没有-r是复制单个文件) mv /etc/aaa /home/bbb 移动/etc下的aaa 到/home下,并且改名bbb;

rm –r 删除一个文件;(如果要是一个文件夹,就有询问yes或no) rm –rf 删除一个文件夹;(如果要是一个文件夹,就无询问) touch 创建文件; pwd 查看当前路径; cd.. 返回相对路径; cd / 返回绝对路径; cd- 返回刚才的路径; su – root或其它用户切换用户; mkdir 创建新目录; cat 查看文件内容; more或less 逐屏查看文件内容; useradd 新添加的用户,在没有更改密码前,无法登陆; passwd 更改密码;但是,密码必须符合复杂性; groupadd 添加一个组; 20150602 w 查看谁登陆过本计算机以及对方的IP; last 查看用户的登录日志; lastlog 查看每个用户最后登录的情况;(一般用于电脑被黑了之后); more /var/log/secure who /var/log/wtmp 干了些什么? root账户下输入su - username 切换到username下输入 history 能看到这个用户历史命令,默认最近的1000条 Linux查看History记录加时间戳小技巧 1.[root@servyou_web ~]# export HISTTIMEFORMAT="%F %T `whoami` " 2.[root@servyou_web ~]# history | tail 3. 1014 2011-06-22 19:17:29 root 15 2011-06-22 19:13:02 root ./test.sh 4. 1015 2011-06-22 19:17:29 root 16 2011-06-22 19:13:02 root vim test.sh 5. 1016 2011-06-22 19:17:29 root 17 2011-06-22 19:13:02 root ./test.sh 6. 1017 2011-06-22 19:17:29 root 18 2011-06-22 19:13:02 root vim test.sh 7. 1018 2011-06-22 19:17:29 root 19 2011-06-22 19:13:02 root ./test.sh 8. 1019 2011-06-22 19:17:29 root 20 2011-06-22 19:13:02 root vim test.sh 9. 1020 2011-06-22 19:17:29 root 21 2011-06-22 19:13:02 root ./test.sh

史上最全linux内核配置详解

对于每一个配置选项,用户可以回答"y"、"m"或"n"。其中"y"表示将相应特性的支持或设备驱动程序编译进内核;"m"表示将相应特性的支持或设备驱动程序编译成可加载模块,在需要时,可由系统或用户自行加入到内核中去;"n"表示内核不提供相应特性或驱动程序的支持。只有<>才能选择M 1. General setup(通用选项) [*]Prompt for development and/or incomplete code/drivers,设置界面中显示还在开发或者还没有完成的代码与驱动,最好选上,许多设备都需要它才能配置。 [ ]Cross-compiler tool prefix,交叉编译工具前缀,如果你要使用交叉编译工具的话输入相关前缀。默认不使用。嵌入式linux更不需要。 [ ]Local version - append to kernel release,自定义版本,也就是uname -r可以看到的版本,可以自行修改,没多大意义。 [ ]Automatically append version information to the version string,自动生成版本信息。这个选项会自动探测你的内核并且生成相应的版本,使之不会和原先的重复。这需要Perl的支持。由于在编译的命令make-kpkg 中我们会加入- –append-to-version 选项来生成自定义版本,所以这里选N。 Kernel compression mode (LZMA),选择压缩方式。 [ ]Support for paging of anonymous memory (swap),交换分区支持,也就是虚拟内存支持,嵌入式不需要。 [*]System V IPC,为进程提供通信机制,这将使系统中各进程间有交换信息与保持同步的能力。有些程序只有在选Y的情况下才能运行,所以不用考虑,这里一定要选。 [*]POSIX Message Queues,这是POSIX的消息队列,它同样是一种IPC(进程间通讯)。建议你最好将它选上。 [*]BSD Process Accounting,允许进程访问内核,将账户信息写入文件中,主要包括进程的创建时间/创建者/内存占用等信息。可以选上,无所谓。 [*]BSD Process Accounting version 3 file format,选用的话统计信息将会以新的格式(V3)写入,注意这个格式和以前的v0/v1/v2 格式不兼容,选不选无所谓。 [ ]Export task/process statistics through netlink (EXPERIMENTAL),通过通用的网络输出工作/进程的相应数据,和BSD不同的是,这些数据在进程运行的时候就可以通过相关命令访问。和BSD类似,数据将在进程结束时送入用户空间。如果不清楚,选N(实验阶段功能,下同)。 [ ]Auditing support,审计功能,某些内核模块需要它(SELINUX),如果不知道,不用选。 [ ]RCU Subsystem,一个高性能的锁机制RCU 子系统,不懂不了解,按默认就行。 [ ]Kernel .config support,将.config配置信息保存在内核中,选上它及它的子项使得其它用户能从/proc/ config.gz中得到内核的配置,选上,重新配置内核时可以利用已有配置Enable access to .config through /proc/config.gz,上一项的子项,可以通过/proc/ config.gz访问.config配置,上一个选的话,建议选上。 (16)Kernel log buffer size (16 => 64KB, 17 => 128KB) ,内核日志缓存的大小,使用默认值即可。12 => 4 KB,13 => 8 KB,14 => 16 KB单处理器,15 => 32 KB多处理器,16 => 64 KB,17 => 128 KB。 [ ]Control Group support(有子项),使用默认即可,不清楚可以不选。 Example debug cgroup subsystem,cgroup子系统调试例子 Namespace cgroup subsystem,cgroup子系统命名空间 Device controller for cgroups,cgroups设备控制器

Linux驱动工程师成长之路

本人此刻还不是什么驱动工程师,连入门都谈不上,但我坚信在未来的3-5年我肯定能成为我想像中的人,因为我马上就要进入这一行工作了。写下这个日志来记录我是怎么最后成为我想像中的人才的,呵呵。 《Linux驱动工程师》这个东西是我在大二的时候看到有一篇讲如何学习嵌入式的,点击这里下载PDF,里面讲到嵌入式分为四层:硬件,驱动,系统,应用程序;还说linux驱动最难然后工资也最高就冲着他这句话我就决定我大学毕业的时候要去做这个linux驱动工程师,随后我就先后买了51单片机,ARM7,ARM9还有一大堆的视频教程准备来进行学习。我还跟我旁边那个哈工大哥们说:“我们学校像我这样的人很少,你们学校呢?”他说:“太少了,不过我们学校都是做这种板子卖的人比较多!”。行,你们牛!即使是买了这些东西,从大二到现在都快毕业了但感觉还是没有入门。回想一下我都学过什么啊:1:自己在ARM9上写bootloader(主要锻炼了三方面的知识:C语言应该写了有近万行的代码,ARM9的外设的基本操作方法如UART,LCD,TOUCH,SD,USB,ETHERNET...,makefile);2:移植和学习linux驱动。下面我说一下我学习Linux驱动的一个思路这也是我在面试的时候自我介绍中最重要的部分;1:硬件知识学习Linux驱动首先得了解这个驱动对应的硬件的一些基本原理和操作方法比如LCD你得了解它的场同步,行同步,像素时钟,一个像素的表示模式,还有就是这个LCD是怎么把图像显示在屏幕上的。如果是USB,SD卡就得了解相关协议。可以通过spec(协议)、datasheet来了解,这就是传说中的Linux驱动开发三件宝之二,还有一个就是linux相关源码。2:了解linux驱动框架linux下的每一类驱动差不多都是一个比较完善的子系统,比如FLASH的驱动它就属于MTD子系统从上到下分为四层:设备节点层,设备层,原始设备层,最下面的与具体硬件相关的硬件驱动层,通常要我们自己来实现就是最下面这个与具体硬件相关那部分代码。3:了解这个驱动的数据流。这个过程与第二个过程紧密相关,如果了解了驱动的框架差不多这个过程也算了解了。比如flash.在/dev/目录下有对应flash的字符设备文件和块设备文件,用户对这些文件进行读、写、ioctl操作,其间通过层层的函数调用最终将调用到最下面的硬件驱动层对硬件进行操作。了解这个过程我相信在调试驱动的时候是很有帮助。3:分析与硬件相关通常需要我们实现的那部分源代码。4:三板子上将驱动调试出来。每次调试都会出问题,但我买的板子提供的资料比较全调试过程中遇到的问题都比较浅显,即使是浅显的问题也要把它记录下来。(这个是我上次在华为面试的时候,那个人问我你调试驱动遇到过什么问题吗?你是如何解决的。当时我学习还没有到调试驱动这一步,所以那次面试也惨败收场)。 好像说了这么多,还没有进入正题《工作的选择》。在年前去了龙芯,实习2.8K,转正3.5k,环境还是不错,经理很好,头儿也很帅都是中科院的硕士。不过去了两周我就没去了身边的人都不太理解,我也一度有过后悔的时候,从龙芯出来应该是1月6号,也就是从那个时候开始我就没有再找工作,转而学习linux驱动。一直到上周日。上周日的晚上我就开始投简历一开始要找linux驱动,在智联里面输入linux驱动出来500来个职位,点开一看没有一个自己符合要求的,差不多都要3-5年经验本科,有时候好不容易有个实习的关键字在里面,一看要求硕士,严重打击了我的信心,哎不管了随便投,最后又投了一下嵌入式关键字的职位。最后就瞎申请,看看职位要求差不多就申请。周一来了,这周一共来了6个面试,创下了我求职以来的历史新高。周一下午面了一家感觉还不错不过到现在也没有给我一个通知,估计当时我要了4500把他给要跑了,这家是做测量的不是Linux驱动,差不多是把ARM当单片机用。周二上午一家也是要招linux驱动面了估计不到二分钟,他

linux读书笔记

12.29 Linux系统 Linux是真正的多用户、多任务操作系统。它继承了UNIX系统的主要特征,具有强大的信息处理功能,特别在Internet和Intranet的应用中占有明显优势。是一个完整的UNIX类操作系统。它允许多个用户同时在一个系统上运行多道程序。真正的32位操作系统。 用户接口 用户接口定义了用户和计算机交互作用的方式。Linux操作系统提供4种不同的用户接口。命令行接口 命令行是为具有操作系统使用经验,熟悉所用命令和系统结构的人员设计的。功能强大,使用方便的命令行是UNIX/Linux系统的一个显著特征。支持命令行的系统程序是命令解释程序。它的主要功能是接收用户输入的命令,然后予以解释并执行。 “$ ”是系统提示符。 在UNIX/Linux系统中,通常将命令解释程序称为shell。各种Linux环境下都安装了多种shell。这些shell由不同的人编写并得到一部分用户的青睐,各有其优势,最常用的几种是Bourne shell(sh),C shell(csh),Bourne Again shell(bash)和Korn shell(ksh)。红旗Linux 的默认shell是bash。 Bash 菜单 图形用户接口 程序接口 程序接口也称为系统调用接口。用户在自己的C程序中使用系统调用,从而获得系统提供的更基层的服务。 系统调用是操作系统内核与用户程序,应用程序之间的接口。在UNIX/Linux系统中,系统调用以C函数的形式出现。例如:fd=fopen(“file1.c”,2);其中,open是系统调用。 所有内核之外的程序都必须经由系统调用才能获得操作系统的服务。系统调用只能在C程序中使用,不能作为命令在终端上执行。由于系统调用能直接进入内核执行,所以其执行效率高。 Linux的版本 Linux有两种版本:核心(Kernel)版本和发行(Distribution)版本。 核心版本 核心版本主要是Linux的内核。Linux内核的官方版本由Linus Torvalds本人维护着。核心版本的序号由三部分数字构成,其形式为:major.minor.patchlevel 其中,major是主版本号,minor是次版本号,二者共同构成了当前核心版本好;patchlevel 表示对当前版本的修订次数。例如:2.6.34表示对2.6核心版本的第34次修订。

Linux内核驱动模块编写概览-ioctl,class_create,device_create

如果你对内核驱动模块一无所知,请先学习内核驱动模块的基础知识。 如果你已经入门了内核驱动模块,但是仍感觉有些模糊,不能从整体来了解一个内核驱动模块的结构,请赏读一下这篇拙文。 如果你已经从事内核模块编程N年,并且道行高深,也请不吝赐教一下文中的疏漏错误。 本文中我将实现一个简单的Linux字符设备,旨在大致勾勒出linux内核模块的编写方法的轮廓。其中重点介绍ioctl的用途。 我把这个简单的Linux字符设备模块命名为hello_mod. 设备类型名为hello_cl ass 设备名为hello 该设备是一个虚拟设备,模块加载时会在/sys/class/中创建名为hello_class 的逻辑设备,在/dev/中创建hello的物理设备文件。模块名为hello_mod,可接受输入字符串数据(长度小于128),处理该输入字符串之后可向外输出字符串。并且可以接受ioctl()函数控制内部处理字符串的方式。 例如: a.通过write函数写入“Tom”,通过ioctl函数设置langtype=chinese,通过read函数读出的数据将会是“你好!Tom/n” b.通过write函数写入“Tom”,通过ioctl函数设置langtype=english,通过read函数读出的数据将会是“hello!Tom/n” c.通过write函数写入“Tom”,通过ioctl函数设置langtype=pinyin,通过read函数读出的数据将会是“ni hao!Tom/n” 一般的内核模块中不会负责设备类别和节点的创建,我们在编译完之后会得到.o或者.k o文件,然后insmod之后需要mk nod来创建相应文件,这个简单的例子 中我们让驱动模块加载时负责自动创建设备类别和设备文件。这个功能有两个步骤, 1)创建设备类别文件class_cr eate(); 2)创建设备文件dev ice_create(); 关于这两个函数的使用方法请参阅其他资料。 linux设备驱动的编写相对wi ndows编程来说更容易理解一点因为不需要处理IR P,应用层函数和内核函数的关联方式浅显易懂。 比如当应曾函数对我的设备调用了open()函数,而最终这个应用层函数会调用我的设备中的自定义open()函数,这个函数要怎么写呢, 我在我的设备中定义的函数名是hello_mod_open,注意函数名是可以随意定义,但是函数签名是要符合内核要求的,具体的定义是怎么样请看 static int hello_mod_open(struct inode *, struct file *); 这样就定义了内核中的open函数,这只是定义还需要与我们自己的模块关联起来,这就要用到一个结构 struct file_operations 这个结构里面的成员是对应于设备操作的各种函数的指针。 我在设备中用到了这些函数所以就如下定义,注意下面的写法不是标准ANSI C的语法,而是GNU扩展语法。 struct file_operations hello_mod_fops = { .owner = THIS_MODULE, .open = hello_mod_open,

从零开始搭建Linux驱动开发环境

参考: 韦东山视频第10课第一节内核启动流程分析之编译体验 第11课第三节构建根文件系统之busybox 第11课第四节构建根文件系统之构建根文件系统韦东山书籍《嵌入式linux应用开发完全手册》 其他《linux设备驱动程序》第三版 平台: JZ2440、mini2440或TQ2440 交叉网线和miniUSB PC机(windows系统和Vmware下的ubuntu12.04) 一、交叉编译环境的选型 具体的安装交叉编译工具,网上很多资料都有,我的那篇《arm-linux- gcc交叉环境相关知识》也有介绍,这里我只是想提示大家:构建跟文件系统中所用到的lib库一定要是本系统Ubuntu中的交叉编译环境arm-linux- gcc中的。即如果电脑ubuntu中的交叉编译环境为arm-linux-

二、主机、开发板和虚拟机要三者互通 w IP v2.0》一文中有详细的操作步骤,不再赘述。 linux 2.6.22.6_jz2440.patch组合而来,具体操作: 1. 解压缩内核和其补丁包 tar xjvf linux-2.6.22.6.tar.bz2 # 解压内核 tar xjvf linux-2.6.22.6_jz2440.tar.bz2 # 解压补丁

cd linux_2.6.22.6 patch –p1 < ../linux-2.6.22.6_jz2440.patch 3. 配置 在内核目录下执行make 2410_defconfig生成配置菜单,至于怎么配置,《嵌入式linux应用开发完全手册》有详细介绍。 4. 生成uImage make uImage 四、移植busybox 在我们的根文件系统中的/bin和/sbin目录下有各种命令的应用程序,而这些程序在嵌入式系统中都是通过busybox来构建的,每一个命令实际上都是一个指向bu sybox的链接,busybox通过传入的参数来决定进行何种命令操作。 1)配置busybox 解压busybox-1.7.0,然后进入该目录,使用make menuconfig进行配置。这里我们这配置两项 一是在编译选项选择动态库编译,当然你也可以选择静态,不过那样构建的根文件系统会比动态编译的的大。 ->Busybox Settings ->Build Options

linux驱动学习笔记LED

LED驱动学习: 是一个char字符类型的驱动 //配置模式为输出端口 static unsigned int led_cfg_table [] = { S3C2410_GPB5_OUTP, S3C2410_GPB6_OUTP, S3C2410_GPB7_OUTP, S3C2410_GPB8_OUTP, }; s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPB5_OUTP); s3c2410_gpio_cfgpin(37, 0x01 << 10); 这个在\arch\arm\mach-s3c2410\include\mach\regs-gpio.h中定义 #define S3C2410_GPB5 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5) #define S3C2410_GPB5_INP (0x00 << 10) #define S3C2410_GPB5_OUTP (0x01 << 10) #define S3C2410_GPB5_nXBACK (0x02 << 10) S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5) #define S3C2410_GPIONO(bank,offset) ((bank) + (offset)) #define S3C2410_GPIO_BANKA (32*0) #define S3C2410_GPIO_BANKB(32*1) static int __init dev_init(void) { int ret; int i; for (i = 0; i < 4; i++) { s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); s3c2410_gpio_setpin(led_table[i], 0); } 在驱动的初始化函数中经常看到,__init 前缀,这个在下面文件中定义 file:/include/linux/init.h ? /* These macros are used to mark some functions or ?* initialized data (doesn't apply to uninitialized data) ?* as `initialization' functions. The kernel can take this ?* as hint that the function is used only during the initialization ?* phase and free up used memory resources after ?* ?* Usage: ?* For functions: ?* ?* You should add __init immediately before the function name, like: ?*

linux笔记

1.ls:查看当前路径下的文件以及文件夹的名字 2.ls /bin:查看根目录下的bin文件夹的东西 3.cd Desktop进入到Desktop文件夹 4.cd ..跳转到当前路径的上一层 5.pwd:显示当前操作的路径(绝对路径) 6.clear:清屏 7.绝对路径:/home/python 8.相对路径:cd downloads 9..表示当前路径 10...表示上一层路径 11.c d -:跳转到上一层所在的路径 12.t ab自动补全 13.t ouch 1.txt 创建文件 14.l s * 表示显示所有文件 15.l s *.txt 表示显示以所有.txt结尾的文件 16.l s*.t[xn]t 表示显示以txt或者tnt结尾的所有文件 17.m ore 查看文件的内容 18.l s–alh | more 查看文件的内容并以管道符号进行连接 19.c d ~切换到当前用户的主目录 20.m kdir 创建文件夹 21.m kdira/b/c –p 连续创建文件夹 22.t ree 以目录数的方式显示

23.r mdir 删除文件夹(必须是空目录) 24.实物图操作的文件不会被删除直接进回收站 25.用命令删除的文件是不会进入回收站的 26.r m 删除文件/文件夹 27.r m haha.txt –r 直接删除文件夹(-r表示递归的删除) 28.r m haha.txt –i 给将删除的文件一个删除提示 29.r m haha.txt –f 强制删除 30.l inux建立链接影响(相当于创建windows下的快捷方式) 31.l n 01.txt 创建快捷方式 32.g edit 01.txt 编辑文件的内容 33.c at 01.txt 查看所编辑的内容 34.c at 01.txt > 02.txt 合并文件 35.g rep–n ‘a’grep.txt 搜素文件当中带a的文件 36.g rep–i ‘a’grep.txt搜素文件当中带a的文件(忽略大小写) 37.–-help 查找帮助文档 38.f ind 查找文件 39.c p a b 将a文件下的内容整体复制到b文件夹下(无效的文 件无法复制) 40.c p a/* b 将a文件夹下的所有内容复制到b文件夹下 41.m v a b 将a文件夹整体移动到b文件夹下 42.–v 显示移动进度 43.–I 表示操作的时候显示的提示(y表示确定)

相关文档
最新文档