Linux 下的I2C子系统

2013.7.16

本文分为两部分,一、设备模型 二、平台相关 。

================================================

第一部分,Linux下的i2c设备模型

我们都知道linux设备模型是分bus,devices,driver来看待的,对于I2C设备而言bus就是I2C了,在bus下面会生成devices和drivers两个目录,这两个目录下分别记录了以I2C为总线的设备以及他们的驱动名字。

另外,I2C在linux中用adapter来表示一个I2C控制器,用client来表示一个挂载控制器下的设备。 接下来会以adapter和client的注册流程来探讨linux下I2C的设备模型是如何组织的。

一、adapter注册流程:

都知道,设备是挂载总线下的,可I2C总线没有所属的总线,它直接与CPU交流,所以被当做platform设备被注册,对于高通的qup i2c设备,注册后在sys目录下的设备模型如下:

1)devices 的注册:

首先platform_device结构

structplatform_devicemsm_gsbi0_qup_i2c_device = {

.name= "qup_i2c",

.id= MSM_GSBI0_QUP_I2C_BUS_ID,

.num_resources= ARRAY_SIZE(gsbi0_qup_i2c_resources),

.resource= gsbi0_qup_i2c_resources,

};

其次

static struct platform_device *msm7627a_surf_ffa_devices[] __initdata = {

&msm_device_dmov,

&msm_device_smd,

&msm_device_uart1,

&msm_device_uart_dm1,

&msm_device_uart_dm2,

&msm_gsbi0_qup_i2c_device,

&msm_gsbi1_qup_i2c_device,

&msm_device_otg,

&msm_device_gadget_peripheral,

&smsc911x_device,

&msm_kgsl_3d0,

};

最后

platform_add_devices(msm7627a_surf_ffa_devices,

ARRAY_SIZE(msm7627a_surf_ffa_devices));—》platform_device_register-》platform_device_add-》device_add-》kobject_add

2)Driver的注册:

首先

static structplatform_driverqup_i2c_driver = {

.probe=qup_i2c_probe,

.remove= __devexit_p(qup_i2c_remove),

.driver= {

.name= "qup_i2c",

.owner= THIS_MODULE,

.pm = &i2c_qup_dev_pm_ops,

.of_match_table = i2c_qup_dt_match,

},

};

其次

platform_driver_register(&qup_i2c_driver)-》driver_register-》bus_add_driver-》kobject_init_and_add

3)platform 总线设备的match过程:

在总线类型里有match方法

struct bus_typeplatform_bus_type= {

.name= "platform",

.dev_attrs= platform_dev_attrs,

.match=platform_match,

.uevent= platform_uevent,

.pm= &platform_dev_pm_ops,

};

注意:在platform总线里面没有实现probe方法,这点跟下面的I2C总线的type方法不一样。

在platform_driver_register的时时候会最终调用到对应类型的match方法,流程如下:

platform_driver_register -》driver_register -》bus_add_driver -》driver_attach -》__driver_attach -》driver_match_device -》platform_match

在platform总线里有3种match形式1,OF style match  2,id table  3,driver name

static int platform_match(struct device *dev, struct device_driver *drv)

{

struct platform_device *pdev = to_platform_device(dev);

struct platform_driver *pdrv = to_platform_driver(drv);

/* Attempt anOF style matchfirst */

if (of_driver_match_device(dev, drv))

return 1;

/* Then try to match against theid table*/

if (pdrv->id_table)

return platform_match_id(pdrv->id_table, pdev) != NULL;

/* fall-back todriver namematch */

return (strcmp(pdev->name, drv->name) == 0);

}

4)probe函数的执行来源:

在设备和驱动match成功后就可以执行probe函数,这个可以从设备和驱动两方面来看。从设备注册来看,在device_add-》bus_probe_device-》device_attach-》__device_attach-》driver_probe_device-》really_probe。从驱动注册来看bus_add_driver-》driver_attach-》__driver_attach-》driver_probe_device-》really_probe,其中really_probe里面有这么一段:

if (dev->bus->probe) {

ret = dev->bus->probe(dev);

if (ret)

goto probe_failed;

} else if (drv->probe) {

ret = drv->probe(dev);

if (ret)

goto probe_failed;

}

这段代码表明,当总线有自己的probe的话就执行总线的probe,没有的话就执行驱动的probe,platform总线没有probe方法,所以这里就直接执行驱动的probe,也就是qup_i2c_probe 。

二、client注册流程

对于每一个I2C设备它们都用client来表示,它们不同于adapter了,它们有亲妈了,就是i2c。注册后在sys目录下的设备模型如下:

从这个devices的设备模型可以看出,不光是client表示的设备被注册在i2c总线下,实际上adapter也以i2c-0和i2c-1的形式在这个目录下有生成一个符号链接。

1)devices 的注册:

在linux中用i2c_devinfo来表示一个I2C设备的信息

structi2c_devinfo{

struct list_headlist;

intbusnum;//bus所在的编号

struct i2c_board_infoboard_info;

};

其中i2c_board_info表示板级文件设备的具体信息

以apds990x的p-sensor为例

static structi2c_board_infoi2c_info_apds990x = {

I2C_BOARD_INFO("apds990x", 0x39),

.platform_data = &apds990x_platformdata,

};

i2c_board_info通过i2c_register_board_info函数 整合到i2c_devinfo中,并把它添加到__i2c_board_list中,当作为platform设备的adapter被注册后,会调用到该adapter所对应的probe函数,在该函数中会调用到i2c_add_numbered_adapter,最终注册那些挂在相应adapter上的设备,以qup为例,调用流程如下:

qup_i2c_probe -》i2c_add_numbered_adapter-》i2c_register_adapter -》i2c_scan_static_board_info-》i2c_new_device-》device_register -》device_add

在i2c_scan_static_board_info中会用到挂载__i2c_board_list上的i2c_devinfo,并在i2c_new_device函数中将i2c_devinfo转化为i2c_client 。

实际上device_add添加的文件节点在sys/devices目录下,在/sys/bus/i2c/devices下的都是创建的指向该节点的符号链接,关于device_add的分析在下面第3)点中会有说明。

device_add生成的I2C-0节点如下:

2)驱动与devices的match

类似与platform总线有个platform_bus_typei2c总线总线也有个i2c_bus_type该结构如下:

struct bus_type i2c_bus_type = {

.name= "i2c",

.match= i2c_device_match,

.probe=i2c_device_probe,

.remove= i2c_device_remove,

.shutdown= i2c_device_shutdown,

.pm= &i2c_device_pm_ops,

};

可以看到与platform_bus_type不同的是i2c_bus_type不但有match方法还有probe方法,也就是在执行到really_probe之时它会执行dev->bus->probe(dev);也就是i2c_bus_type自己实现的probe方法i2c_device_probe。在i2c_device_probe函数中做了两件事,1,通过dev结构体找到i2c_client,2,然后又通过driver->probe(client, i2c_match_id(driver->id_table, client));函数调用到driver自己的probe,但是这个probe的参数是client了,而前面qup_i2c_probe

的参数是dev。

3)在/sys/bus/i2c/devices代表adatper的i2c-0和i2c-1是怎么来的?device_add解析

在i2c_register_adapter中,有dev_set_name(&adap->dev, "i2c-%d", adap->nr);将adapter的名字按照adapter的序号命名为:i2c-0 i2c-1,然后通过device_register注册,实际上,该函数注册的真正节点只有一个在/sys/devices目录下,在/sys/bus/i2c/devices目录下的i2c-0实际上是在device_add的时候调用bus_add_device生成的符号链接。

这个函数如下:

int bus_add_device(struct device *dev)

{

struct bus_type *bus = bus_get(dev->bus);

int error = 0;

if (bus) {

pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));

error = device_add_attrs(bus, dev);

if (error)

goto out_put;

error =sysfs_create_link(&bus->p->devices_kset->kobj,

&dev->kobj, dev_name(dev));

if (error)

goto out_id;

error =sysfs_create_link(&dev->kobj,

&dev->bus->p->subsys.kobj, "subsystem");

if (error)

goto out_subsys;

klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

}

这个函数的功能是在sys中添加两个链接:一个在总线目录下指向设备,另一个在设备的目录下指向总线子系统。在该函数的末尾,会添加该设备到klist_devices链表,这个链表管理着这条总线上的所有设备,那么既然这样,我们就可以通过这个链表来找到挂载在该链表上的任何一个设备,这个功能很有用,比如休眠的时候,我们要休眠某条总线上的设备,就可以通过遍历这个链表来实现。对应某条bus上的klist_devices链表我们可以通过bus_get_device_klist这个API来获取。与klist_devices对应的一个字段是klist_drivers,功能与其相似。同理,在.sys/class/i2c-adapter/目录下生成的i2c-0 i2c-1节点也是在device_add中调用device_add_class_symlinks函数创建的符号链接。

节点如下:

综上,在linux每通过device_add添加一个设备的时候,不光会在sys文件系统的devices目录下添加目录,一般也会在class和bus目录下建立相应的符号链接,这三个目录分别代表了对驱动设备的三种不同观察角度,但实际上bus和class指向都是devices目录下的节点。

4)字符设备与应用层操作,i2c-dev.c文件解析

到目前为止,前面说的4个重要文件已经提到了3个,但我们还没有用到任何一个定义在i2c-dev.c文件中的api,那么这个文件是干嘛的呢?在看makefile的时候,我们看到i2c-dev.c的config是这样的obj-$(CONFIG_I2C_CHARDEV)+= i2c-dev.o ,顾名思义,这个文件实现的是I2C作为字符设备的功能。

好吧,既然是字符设备,那么肯定得调用字符设备的通用接口向系统注册它,在i2c_dev_init函数中register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);,从这个函数有两个重要参数在下面都会用到,第一个是主设备号,第二个是文件操作结构。主设备号会在接下来sys向上层发送uevent的时候用到,用以将该字符节点与adapter对应的驱动文件联系起来,而文件操作结构则向上层提供操作接口。

i2cdev_fops如下:

static const struct file_operationsi2cdev_fops= {

.owner= THIS_MODULE,

.llseek= no_llseek,

.read= i2cdev_read,

.write= i2cdev_write,

.unlocked_ioctl= i2cdev_ioctl,

.open= i2cdev_open,

.release= i2cdev_release,

};

这里先提一句,上层操作的流程一般分两步:

1.open设备/dev/i2c-0获取文件句柄,实际上是获取adapter的信息

2.Ioctl里面传递要读或者写的数据或地址,以及连接在该adapter上要读或者写设备的地址。

上面提到的open /dev/i2c-0那么这个代表adapter的节点是怎么建立的呢?

看下i2c-dev.c在sys上建立起的节点:

在/dev 目录下建立的节点

Dev 目录下的节点也就对应两个adapter,我们可以叫他adapter0和adapter1。

建立的流程如下:

i2c_dev_init -》class_create(THIS_MODULE, "i2c-dev"); -》i2c_for_each_dev(NULL, i2cdev_attach_adapter);  -》i2cdev_attach_adapter-》device_create(i2c_dev_class, &adap->dev,MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d", adap->nr);

其中,class_create建立./class/i2c-dev目录,然后device_create函数按照adapter的编号在该目录下建立i2c-0和i2c-1。那么adapter0和adapter1怎么在/dev/目录下建立起/dev/i2c-0和/dev/i2c-1的节点的呢?这是因为device_create最终调用了device_add,这个函数不但在sys目录下建立了./sys/class/i2c-dev/i2c-0 ./sys/class/i2c-dev/i2c-1,它还调用kobject_uevent(&dev->kobj, KOBJ_ADD);向上层的udev进程发送了uevent的设备添加信息。udev进程会根据该信息在在dev目录下自动创建对应设备的文件。在i2c-dev层,找到一个adapter就会自动为其创建设备节点,形式类似于i2c-*,那么当应用层open对应的设备节点的时候,内核会自动调用注册的字符设备的操作函数。Kernel 2.6 以后的版本,无论字符设备还是块设备的设备节点的建立都是依赖于sys 文件系统的。

前面已经提到,应用层会先调用open函数,获取文件句柄,也就是对应adapter的信息,然后调用ioctl函数传送数据,那我们就按照用户层的流程先看一下open函数i2cdev_open:

static int i2cdev_open(struct inode *inode, struct file *file)

{

unsigned int minor = iminor(inode);//得到次设备号

struct i2c_client *client;

struct i2c_adapter *adap;

struct i2c_dev *i2c_dev;//注意这个结构,一个i2c_dev结构代表一个adapter

i2c_dev = i2c_dev_get_by_minor(minor);//实际上是通过次设备号获取了对应adapter

if (!i2c_dev)

return -ENODEV;

adap = i2c_get_adapter(i2c_dev->adap->nr);//同上

if (!adap)

return -ENODEV;

/* This creates an anonymous i2c_client, which may later be

* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.

*

* This client is ** NEVER REGISTERED ** with the driver model

* or I2C core code!!  It just holds private copies of addressing

* information and maybe a PEC flag.

*/

client = kzalloc(sizeof(*client), GFP_KERNEL);//一个client代表一个从设备

if (!client) {//这里显然没标明是哪个从设备,因为没有从设备的地址

i2c_put_adapter(adap);

return -ENOMEM;

}

snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

client->adapter = adap;

file->private_data = client;//client的信息还不完善,暂留着,等后面的ioctl来填充

return 0;

}

限于篇幅,就补贴ioctl的源代码了,总之,在i2cdev_ioctl中会根据上层传来的信息进一步完善前面open中建立的client结构,使之拥有能代表一个从设备的功能。然后调用read/write开始传输信息。

以read为例:

static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,

loff_t *offset)

{

char *tmp;

int ret;

struct i2c_client *client = file->private_data;//client就这么用的

if (count > 8192)

count = 8192;

tmp = kmalloc(count, GFP_KERNEL);

if (tmp == NULL)

return -ENOMEM;

pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",

iminor(file->f_path.dentry->d_inode), count);

ret = i2c_master_recv(client, tmp, count);//调用这个函数开始数据传递

if (ret >= 0)

ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;

kfree(tmp);

return ret;

}

int i2c_master_recv(const struct i2c_client *client, char *buf, int count)

{

struct i2c_adapter *adap = client->adapter;

struct i2c_msg msg;

int ret;

msg.addr = client->addr;// 这里client所有的信息都是用户通过ioctl设的

msg.flags = client->flags & I2C_M_TEN;

msg.flags |= I2C_M_RD;

msg.len = count;

msg.buf = buf;

ret = i2c_transfer(adap, &msg, 1);//这个函数会调用adap->algo->master_xfer来实际

//平台相关的控制器来传送数据,对于QUP控制器,.master_xfer= qup_i2c_xfer

/* If everything went ok (i.e. 1 msg transmitted), return #bytes

transmitted, else error code. */

return (ret == 1) ? count : ret;

}

下面给出一个上层通过/dev/i2c-0来操作I2C的示例:

fd=open("/dev/i2c-0",O_RDWR);

ioctl(fd,I2C_TIMEOUT,100);/*超时时间*/

ioctl(fd,I2C_RETRIES,2);/*重复次数*/

/***write data todevice**/

data.nmsgs=1;

(data.msgs[0]).len=2; //1个写入目标的地址和1个数据

(data.msgs[0]).addr=0x5c;//设备地址

(data.msgs[0]).flags=0; //write

(data.msgs[0]).buf=(unsigned char*)malloc(2);

(data.msgs[0]).buf[0]=0x01;// 写入目标的地址

(data.msgs[0]).buf[1]=0x74;//the data to write

ret=ioctl(fd,I2C_RDWR,(unsigned long)&data);

第二部分, 平台相关

前面两部分都是与平台无关的,对于i2c而言不同的adapter使用不同的驱动,在driver/i2c/busses目录下有许多已经写好的adapter的驱动文件,对于高通集成在45450_kernel中的代码而言使用的是QUP adapter。

一、平台相关资源

static struct resource gsbi0_qup_i2c_resources[] = {

{

.name= "qup_phys_addr",//在probe中被转化为qup_mem通过ioremap赋值//给dev->base作为QUP registers的起始地址80_VM158_2_  page 339

.start= MSM_GSBI0_QUP_PHYS,

.end= MSM_GSBI0_QUP_PHYS + SZ_4K - 1,

.flags= IORESOURCE_MEM,

},

{

.name= "gsbi_qup_i2c_addr",

//在probe中通过ioremap赋值/dev->gsbi作为GSBI_CTRL registers的起始地址

.start= MSM_GSBI0_PHYS,// GSBI_BASE + 0x00000 参考文档80_VM158_2_

.end= MSM_GSBI0_PHYS + SZ_4K - 1,

.flags= IORESOURCE_MEM,

},

{

.name= "qup_err_intr",//i2c 控制器的内部中断

.start= INT_PWB_I2C,

.end= INT_PWB_I2C,

.flags= IORESOURCE_IRQ,

},

};

static struct msm_i2c_platform_data msm_gsbi0_qup_i2c_pdata = {

.clk_freq= 100000,//clock 100k-400k 低于100k的建议用gpio模拟

.msm_i2c_config_gpio= gsbi_qup_i2c_gpio_config,//qup 的不用配gpio

};

二、adapter 的i2c_algorithm

I2C 子系统的中心是adapter(控制器),离开了adapter其他的一切都是空中楼阁。首先,所有client都必须挂载在adapter下,其次,所有client与CPU的通信都是由adapter下的i2c_algorithm来实现的。i2c_algorithm结构是一个控制器的核心,对于不同的控制器,它们之间最大的不同也就是这个结构里面的函数。这个结构如下:

struct i2c_algorithm {

int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,

int num);

int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,

unsigned short flags, char read_write,

u8 command, int size, union i2c_smbus_data *data);

u32 (*functionality) (struct i2c_adapter *);

};

里面有三个函数:

1,functionality:I2C规范定义了许多功能,但不是所有的I2C或者SMBus适配器实现了I2C规范上的所有功能,因此当访问I2C适配器时,并不能完全假定适配器提供了你所需的功能。the client需要有一种检测适配器是否提供了所需功能的方法。举个例子:

比如,bma250 probe函数首先就调用i2c_check_functionality检查该adapter是否支持该devices

if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {

printk(KERN_INFO "i2c_check_functionality error\n");

goto exit;

}

也就是说,对于一个设备而言,只有在其所在的adapter满足这个基本条件的前提下,后续的通信操作才有意义。限于篇幅,就不分析源码了,很简单。

2,smbus_xfer:

Smbus 其实相当于I2C的一个子集,是与I2C协议完全兼容的,最初被用在电源管理上,其适合一些对传输速度和数据量要求低的外设。

SMBus与I2C的比较:

SMBus

I2C

最大传输速度 100kHz

最大传输速度400kHz

最小传输速度 10kHz

无最小传输速度

35ms时钟低超时

无时钟超时

固定的逻辑电平

逻辑电平由VDD决定

不同的地址类型(保留、动态等) 7位、

10位和广播呼叫从地址类型

不同的总线协议(快速命令、

处理呼叫等)无总线协议

s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,

char read_write, u8 command, int protocol,

union i2c_smbus_data *data)

{

unsigned long orig_jiffies;

int try;

s32 res;

flags &= I2C_M_TEN | I2C_CLIENT_PEC;

if (adapter->algo->smbus_xfer) {

...

}else

res =i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,

command, protocol, data);

return res;

}

status =i2c_transfer(adapter, msg, num);

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

...

if(adap->algo->master_xfer) {

} else {

...

}

}

static const structi2c_algorithm qup_i2c_algo= {

.master_xfer= qup_i2c_xfer,

.functionality= qup_i2c_func,

};

这里有个函数i2c_smbus_write_byte_data()的调用流程图:

3,master_xfer:

对于QUP而言,master_xfer函数是qup_i2c_xfer,所有挂载在QUP上的设备,与CPU通信都靠这个函数。我们先看一副图,以便对QUP i2c有个总体把握,然后我们去阅读源码:

为什么会用INPUT OUTPUT BUFFER呢?应该是当用于block数据传输时不用每一字节都去中断CPU,有提高效率 和省电的作用。

FIFO 与中断

In FIFO_Mode, if the programmedQUP_MX_READ_COUNTis zero, the SW isinterrupted every timethe QUP Input FIFO “goes”fromempty to non-emptystate. If the count is non-zero, then the SW isinterrupted only onceper RUN_STATE when theprogrammed number of wordsare shifted in the FIFO from outside.

In FIFO_Mode, if the programmedQUP_MX_WRITE_COUNTis zero, the SW isinterrupted every timethe QUP Output FIFO “goes”fromnon-empty to emptystate. If the count is non-zero, then the SW isinterrupted only onceper RUN_STATE when theprogrammed number of wordswritten into the Output FIFO are sent out of the Output FIFO.

QUP_MX_WRITE_COUNT:

What theQUP_MX_OUTPUT_COUNTregister means to Block_Mode and Data_Mover_Mode, this register means the same to FIFO_mode. this register means the same toFIFO_mode. If this register is non-zero,thenthe gsbi_qup_irq is assertedaftershifting in the number of shifts specified by this register.

从上图可以看到,CPU与外设数据的交互是靠中断来推动的。无论哪种模式下,中断会发生在预先写的数据个数后才会发生,中断里面做了什么呢,接下来我们去看源码,主要集中在 qup_i2c_xfer    qup_i2c_interrupt这两个函数上

接下来看下代码。。。

三、一些常见问题

1,如果怀疑是adapter相关的问题,可以搜索下I2c-qup.c (drivers\i2c\busses)文件中dev_err打印出来的信息,比如出现在传输过程中timeout或者 从设备no ack的情况,在这个文件的qup_i2c_xfer函数中都会用dev_err的形式打印出来。

比如 在该函数中

dev_err(dev->dev,"I2C slave addr:0x%x not connected\n",dev->msg->addr);

如果发送数据出现no ack就会打印出该设备不存在,这是通过检测QUP_I2C_NACK_FLAG标准位来判断的,可以看到在整个adapter的驱动代码中看不到软件有置位该bit的动作,可以判断它是由硬件自动实现的。

2,I2C被拉死的问题。

讲下现象,以及定位方法。

I2c 平时默认高电平,clock为高时,data不能改变,如果不小心,这会在某些特殊情况下造成I2C被拉死。

比如,重启。

主控传输完8bit后Master交出data的控制权,等待从设备应答,slave拉低数据线,刚好这时,主板重启,clock还是主控,所以clock被拉高(上拉电阻),数据线是从设备控制的,如果此时,从设备没有重启的话,data线就还是从设备控制,因为clock一直是高了,slave不能将data拉高,所以造成死锁。

解决方法,

1)让从设备跟主板一起重启。

2)主控检测到这种状况,就通过某种特殊协定让从设备放弃data线,比如连发9个clock。

3,一些I2C从设备无应答,一般先检查上电和上电时序,如果还是无应答,那么换个板子,换颗IC这样的方法可用于定位是I2C还是板子的问题。如果这些都作了还是解决不了,就抓波形出来看。

linux i2c子系统看不懂啊,Linux 下的I2C子系统相关推荐

  1. 学习Linux命令神器-看不懂直接给你解释

    大家都知道,Linux 系统有非常多的命令,而且每个命令又有非常多的用法,想要全部记住所有命令的所有用法,恐怕是一件不可能完成的任务. 一般情况下,我们学习一个命令时,要么直接百度去搜索它的用法,要么 ...

  2. linux的课程完全看不懂,学习Linux命令神器-看不懂直接给你解释

    导读 大家都知道,Linux 系统有非常多的命令,而且每个命令又有非常多的用法,想要全部记住所有命令的所有用法,恐怕是一件不可能完成的任务. 一般情况下,我们学习一个命令时,要么直接百度去搜索它的用法 ...

  3. linux中怎么看挂载文件系统,Linux中如何查看已挂载的文件系统类型详解

    前言 如你所知,Linux 支持非常多的文件系统,例如 ext4.ext3.ext2.sysfs.securityfs.FAT16.FAT32.NTFS 等等,当前被使用最多的文件系统是 ext4.你 ...

  4. linux gpt分区看不到,Linux无法看到我的任何分区 – 备份GPT表不在磁盘的末尾

    我正在尝试在HP Pavilion 14英寸超极本上安装Linux,但没有任何成功. 起初我尝试在其上安装Ubuntu;一切都很顺利,我进入了Live DVD(是的,我就像那样老了),然后去我的磁盘上 ...

  5. linux 的ss看进程号,linux ss命令详解

    ss 是 Socket Statistics 的缩写.ss 命令可以用来获取 socket 统计信息,它显示的内容和 netstat 类似.但 ss 的优势在于它能够显示更多更详细的有关 TCP 和连 ...

  6. linux字符终端看视频,在Linux终端上看电影很酷吗?

    时至今日,随处可见高分辨率的视频(youtube上也是如此),只有那些真正的怪人才会去使用ascii文本来在终端上播放电影.不过令人惊讶的是,一些视频用这种方法竟然效果还行! 我发现动画视频的显示效果 ...

  7. 外文试卷看不懂?在线翻译器拍照了解一下

    在学习外语的过程中,我们难免会遇到不懂外文试卷的情况.这种情况下,我们应该怎么办呢?本文将介绍一些解决方法,以及一些好用的手机拍照翻译方法. 1. 使用翻译应用 在现如今的数字化时代,有很多翻译软件可 ...

  8. linux系统下编写I2C驱动

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  9. S3C2440 Linux下的I2C驱动以及I2C体系下对EEPROM进行读写操作。

    这篇文档算上期末复习这段时间其实拖了有好久了,因为从一开始接触linux的i2c驱动体系我就各种凌乱,因为起初脑海中既没有整体框架也不熟悉相关体系下的结构,所以四处乱看,经常性的在看内核代码时看着看着 ...

最新文章

  1. Linux内核源代码分析-第三章 内核体系结构概述-1
  2. 【mybatis】IF判断的坑 (实现if test= status == 'zhangsan')
  3. eos和以太坊有什么关系_比特币、以太坊、柚子三者的关系
  4. 部署在Netweaver上的Fiori应用的WebContent文件夹
  5. yum安装最新的 LNMP
  6. 从底层重学 Java 之 BigInteger 大整数 Gitchat连接
  7. Redis慢日志问题解决
  8. 用于Ubuntu 16.04和18.04的TensorFlow,Keras,Caffe,Caffe,CUDA,cuDNN和NVIDIA驱动程序的单行安装
  9. 利用Delphi-cross-socket 库提升kbmmw 跨平台开发
  10. 【长篇连载】桌面管理演义 尾声
  11. Android 中文字体的设置方法和使用技巧
  12. 15-07-22 数据库--存储过程、触发器
  13. 当Java遇上机密计算
  14. angular自带的一些api_在Angular软件中执行API请求的正确方式,了解一下
  15. php debugdumpparams,PHP PDOStatement::debugDumpParams讲解
  16. Android 学习之路
  17. python建模全步骤
  18. macOS 10.14安装win10教程 bootcamp篇
  19. Ubuntu中安装ns3
  20. 十万个为什么哪个版本适合小学生阅读

热门文章

  1. 每日一皮:一个项目开发的真实写照...
  2. 两种写法的效果一样,那么到底哪一种更好呢?
  3. 在生产中使用Java 11:需要了解的重要事项
  4. python 查看字符编码,[Python]判断系统编码和字符编码chardet
  5. 新疆那些大学在计算机专业好,新疆哪些二本大学的计算机科学与技术专业最好?...
  6. 2D目标检测CVPR2020总结
  7. 背景减除(Background Segment)
  8. socket传输结构体,c++,发送OK,recv返回字节大小正确但接受数据为空
  9. python多进程重复加载
  10. pytorch 归一化 测试(BatchNorm2d)