5. 打开设备(第二部分)

5.6 snd_pcm_runtime

struct snd_pcm_runtime {
	/* -- Status -- */
	struct snd_pcm_substream *trigger_master;
	struct timespec64 trigger_tstamp;	/* trigger timestamp */
	bool trigger_tstamp_latched;     /* trigger timestamp latched in low-level driver/hardware */
	int overrange;
	snd_pcm_uframes_t avail_max;
	snd_pcm_uframes_t hw_ptr_base;	/* Position at buffer restart */
	snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
	unsigned long hw_ptr_jiffies;	/* Time when hw_ptr is updated */
	unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */
	snd_pcm_sframes_t delay;	/* extra delay; typically FIFO size */
	u64 hw_ptr_wrap;                /* offset for hw_ptr due to boundary wrap-around */

	/* -- HW params -- */
	snd_pcm_access_t access;	/* access mode */
	snd_pcm_format_t format;	/* SNDRV_PCM_FORMAT_* */
	snd_pcm_subformat_t subformat;	/* subformat */
	unsigned int rate;		/* rate in Hz */
	unsigned int channels;		/* channels */
	snd_pcm_uframes_t period_size;	/* period size */
	unsigned int periods;		/* periods */
	snd_pcm_uframes_t buffer_size;	/* buffer size */
	snd_pcm_uframes_t min_align;	/* Min alignment for the format */
	size_t byte_align;
	unsigned int frame_bits;
	unsigned int sample_bits;
	unsigned int info;
	unsigned int rate_num;
	unsigned int rate_den;
	unsigned int no_period_wakeup: 1;

	/* -- SW params -- */
	int tstamp_mode;		/* mmap timestamp is updated */
  	unsigned int period_step;
	snd_pcm_uframes_t start_threshold;
	snd_pcm_uframes_t stop_threshold;
	snd_pcm_uframes_t silence_threshold; /* Silence filling happens when
						noise is nearest than this */
	snd_pcm_uframes_t silence_size;	/* Silence filling size */
	snd_pcm_uframes_t boundary;	/* pointers wrap point */

	snd_pcm_uframes_t silence_start; /* starting pointer to silence area */
	snd_pcm_uframes_t silence_filled; /* size filled with silence */

	union snd_pcm_sync_id sync;	/* hardware synchronization ID */

	/* -- mmap -- */
	struct snd_pcm_mmap_status *status;
	struct snd_pcm_mmap_control *control;

	/* -- locking / scheduling -- */
	snd_pcm_uframes_t twake; 	/* do transfer (!poll) wakeup if non-zero */
	wait_queue_head_t sleep;	/* poll sleep */
	wait_queue_head_t tsleep;	/* transfer sleep */
	struct fasync_struct *fasync;
	bool stop_operating;		/* sync_stop will be called */
	struct mutex buffer_mutex;	/* protect for buffer changes */

	/* -- private section -- */
	void *private_data;
	void (*private_free)(struct snd_pcm_runtime *runtime);

	/* -- hardware description -- */
	struct snd_pcm_hardware hw;
	struct snd_pcm_hw_constraints hw_constraints;

	/* -- timer -- */
	unsigned int timer_resolution;	/* timer resolution */
	int tstamp_type;		/* timestamp type */

	/* -- DMA -- */           
	unsigned char *dma_area;	/* DMA area */
	dma_addr_t dma_addr;		/* physical bus address (not accessible from main CPU) */
	size_t dma_bytes;		/* size of DMA area */

	struct snd_dma_buffer *dma_buffer_p;	/* allocated buffer */
	unsigned int buffer_changed:1;	/* buffer allocation changed; set only in managed mode */

	/* -- audio timestamp config -- */
	struct snd_pcm_audio_tstamp_config audio_tstamp_config;
	struct snd_pcm_audio_tstamp_report audio_tstamp_report;
	struct timespec64 driver_tstamp;

#if IS_ENABLED(CONFIG_SND_PCM_OSS)
	/* -- OSS things -- */
	struct snd_pcm_oss_runtime oss;
#endif
};

名字

类型

作用

举例

trigger_master

snd_pcm_substream

指向所属的substream对象

trigger_tstamp

timespec64

当音频开始,暂停,停止的时候都会记录时间

trigger_tstamp_latched

bool

是否要从底层的硬件得到时间

overrange

int

录制过程中,ADC转换超出范围的次数

好像没几个硬件支持该功能

avail_max

snd_pcm_uframes_t

记录avail的最大值

返回给用户空间的状态有该值,不是太明白有什么用

hw_ptr_base

snd_pcm_uframes_t

hw_ptr的基地址

介绍snd_pcm_update_hw_ptr0的时候详细介绍

hw_ptr_interrupt

snd_pcm_uframes_t

发生中断的时候,记录的hw_ptr

介绍snd_pcm_update_hw_ptr0的时候详细介绍

hw_ptr_jiffies

unsigned long

记录上次中断发生时的时钟计数

介绍snd_pcm_update_hw_ptr0的时候详细介绍

hw_ptr_buffer_jiffies

unsigned long

DMA缓冲中的数据,传送一轮需要的时钟计数

介绍snd_pcm_update_hw_ptr0的时候会有介绍

delay

snd_pcm_sframes_t

也是用于更新hw_ptr的时候使用,允许一定的偏差

介绍snd_pcm_update_hw_ptr0的时候会有介绍

hw_ptr_wrap

u64

超过boundary,也继续累加

用于时间统计,事实上没什么用

access

snd_pcm_access_t

访问方式,比如直接读写还是使用mmap

SNDRV_PCM_ACCESS_MMAP_INTERLEAVED

SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED

SNDRV_PCM_ACCESS_MMAP_COMPLEX

SNDRV_PCM_ACCESS_RW_INTERLEAVED

SNDRV_PCM_ACCESS_RW_NONINTERLEAVED

format

snd_pcm_format_t

pcm数据格式

SNDRV_PCM_FORMAT_S16_LE

SNDRV_PCM_FORMAT_S16_BE

...

subformat

snd_pcm_subformat_t

pcm数据子格式,这个参数现在没什么用。

SNDRV_PCM_SUBFORMAT_STD

只有这个变量,值是0。

rate

unsigned int

数据采样率

48KHZ,16KHZ

channels

unsigned int

声道数

period_size

snd_pcm_uframes_t

一个周期包含的字节数

也就是每次中断发生前,写到内存中的字节数

periods

unsigned int

dma内存可以存放几个周期的数据

最少是2个,这样才能保证一个周期数据写完后,另一个周期可以马上开始。

buffer_size

snd_pcm_uframes_t

dma buffer大小

period_size * periods 的值

min_align

snd_pcm_uframes_t

一次最少读几帧数据

正常都是1帧。看代码的意思,是为了处理pcm数据不按字节占位的情形,比如每个数据占7字节,就需要读2帧数据。

byte_align

size_t

字节对齐

frame_bits

unsigned int

一帧数据占用的bit数

sample_bits * channels

sample_bits

unsigned int

一个数据占用的bit数

info

unsigned int

硬件信息

SNDRV_PCM_INFO_MMAP

SNDRV_PCM_INFO_MMAP_VALID

....

rate_den

unsigned int

倍率分母

不是1的时候,会放慢播放节奏。

rate_num

unsigned int

倍率分子

不是1的时候,加快播放节奏

no_period_wakeup: 1

unsigned int

是否支持传输中断

tstamp_mode

int

是否允许记录时间

enum {

SNDRV_PCM_TSTAMP_NONE = 0,

SNDRV_PCM_TSTAMP_ENABLE,

SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_ENABLE,

};

period_step

unsigned int

默认设置为1

没看到有什么具体用处

start_threshold

snd_pcm_uframes_t

开始播放的阈值

用来控制写满几个peroid,才去播放,防止破音的出现

stop_threshold

snd_pcm_uframes_t

结束播放的阈值

与start_threshold类似

silence_threshold

snd_pcm_uframes_t

静音的阈值

silence_size

snd_pcm_uframes_t

可以设置一段时间静音

boundary

snd_pcm_uframes_t

介绍过了,hw_ptr可以达到的最大值

silence_start

snd_pcm_uframes_t

记录静音开始

silence_filled

snd_pcm_uframes_t

记录静音结束

sync

union snd_pcm_sync_id

设置硬件的同步ID

没明白有什么用

status

struct snd_pcm_mmap_status *

当前的状态信息

control

struct snd_pcm_mmap_control *

控制信息

twake

snd_pcm_uframes_t

读写的时候,有多少可用period的时候,唤醒等待线程

一般都设置为1

sleep

wait_queue_head_t

poll 时候用到的等待变量

tsleep

wait_queue_head_t

读写操作时候用到的等待变量

fasync

struct fasync_struct *

针对异步通知

stop_operating

bool

记录是否停止播放

buffer_mutex

struct mutex

针对内存操作用到的mutex对象

private_data

void *

驱动可以保存一些自己的数据

HDA中:

runtime->private_data = azx_dev;

 (*private_free)(struct snd_pcm_runtime *runtime)

void

用于清除private_data的函数指针

hw

struct snd_pcm_hardware

保存硬件信息

hw_constraints

struct snd_pcm_hw_constraints

硬件参数约束条件

timer_resolution

unsigned int

没太明白

tstamp_type

int

时间类型

enum {

SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, /* gettimeofday equivalent */

SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, /* posix_clock_monotonic equivalent */

SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW,    /* monotonic_raw (no NTP) */

SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW,

};

dma_area

unsigned char *

dma 缓冲的地址

dma_addr

dma_addr_t

dma缓冲的物理地址

dma_bytes

size_t

dma 缓冲的长度

dma_buffer_p

struct snd_dma_buffer *

预先分配的dma缓冲

buffer_changed:1

unsigned int

标记dma缓冲是否发生变化

一般驱动都不关注它。有些可能是驱动自己生成的缓冲区,不希望被改变。

audio_tstamp_config

struct snd_pcm_audio_tstamp_config

用于调试

https://www.kernel.org/doc/html/latest/sound/designs/timestamping.html

audio_tstamp_report

struct snd_pcm_audio_tstamp_report

用于调试

https://www.kernel.org/doc/html/latest/sound/designs/timestamping.html

driver_tstamp

struct timespec64

用于调试

https://www.kernel.org/doc/html/latest/sound/designs/timestamping.html

• stauts 和 control被设置到mmap内存中,目的是可以和用户空间共享信息。讲述文件读写的时候详细展开其中的内容。

struct __snd_pcm_mmap_status {
	snd_pcm_state_t state;		/* RO: state - SNDRV_PCM_STATE_XXXX */
	int pad1;			/* Needed for 64 bit alignment */
	snd_pcm_uframes_t hw_ptr;	/* RO: hw ptr (0...boundary-1) */
	struct __snd_timespec tstamp;	/* Timestamp */
	snd_pcm_state_t suspended_state; /* RO: suspended stream state */
	struct __snd_timespec audio_tstamp; /* from sample counter or wall clock */
};

struct __snd_pcm_mmap_control {
	snd_pcm_uframes_t appl_ptr;	/* RW: appl ptr (0...boundary-1) */
	snd_pcm_uframes_t avail_min;	/* RW: min available frames for wakeup */
};

 • 硬件信息

struct snd_pcm_hardware {
	unsigned int info;		/* SNDRV_PCM_INFO_* */
	u64 formats;			/* SNDRV_PCM_FMTBIT_* */
	unsigned int rates;		/* SNDRV_PCM_RATE_* */
	unsigned int rate_min;		/* min rate */
	unsigned int rate_max;		/* max rate */
	unsigned int channels_min;	/* min channels */
	unsigned int channels_max;	/* max channels */
	size_t buffer_bytes_max;	/* max buffer size */
	size_t period_bytes_min;	/* min period size */
	size_t period_bytes_max;	/* max period size */
	unsigned int periods_min;	/* min # of periods */
	unsigned int periods_max;	/* max # of periods */
	size_t fifo_size;		/* fifo size in bytes */
};

根据hda_controler.c中的代码来理解它们的含义:

static const struct snd_pcm_hardware azx_pcm_hw = {
	.info =			(SNDRV_PCM_INFO_MMAP |
				 SNDRV_PCM_INFO_INTERLEAVED |
				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
				 SNDRV_PCM_INFO_MMAP_VALID |
				 /* No full-resume yet implemented */
				 /* SNDRV_PCM_INFO_RESUME |*/
				 SNDRV_PCM_INFO_PAUSE |
				 SNDRV_PCM_INFO_SYNC_START |
				 SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
				 SNDRV_PCM_INFO_HAS_LINK_ATIME |
				 SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
	.rates =		SNDRV_PCM_RATE_48000,
	.rate_min =		48000,
	.rate_max =		48000,
	.channels_min =		2,
	.channels_max =		2,
	.buffer_bytes_max =	AZX_MAX_BUF_SIZE,
	.period_bytes_min =	128,
	.period_bytes_max =	AZX_MAX_BUF_SIZE / 2,
	.periods_min =		2,
	.periods_max =		AZX_MAX_FRAG,
	.fifo_size =		0,
};

这里包含硬件的一些基本信息:比如支不支持mmap,数据格式,支持的数据采样率等等。

struct snd_pcm_hw_constraints {
	struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - 
			 SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];
	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
			     SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
	unsigned int rules_num;
	unsigned int rules_all;
	struct snd_pcm_hw_rule *rules;
};

这里也是硬件的基本信息。逐个看以下它们的具体作用。

1. snd_mask

#define SNDRV_MASK_MAX	256

struct snd_mask {
	__u32 bits[(SNDRV_MASK_MAX+31)/32];
};

其实snd_mask的定义是 unsigned int bits[8],也就是8位无符号整数,可以代表256位。每位可以用来表示硬件是否支持某项功能,比如:

#define	SNDRV_PCM_ACCESS_MMAP_INTERLEAVED	((__force snd_pcm_access_t) 0) /* interleaved mmap */
#define	SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED	((__force snd_pcm_access_t) 1) /* noninterleaved mmap */
#define	SNDRV_PCM_ACCESS_MMAP_COMPLEX		((__force snd_pcm_access_t) 2) /* complex mmap */
#define	SNDRV_PCM_ACCESS_RW_INTERLEAVED		((__force snd_pcm_access_t) 3) /* readi/writei */
#define	SNDRV_PCM_ACCESS_RW_NONINTERLEAVED	((__force snd_pcm_access_t) 4) /* readn/writen */
#define	SNDRV_PCM_ACCESS_LAST		SNDRV_PCM_ACCESS_RW_NONINTERLEAVED

在支持交错的mmap操作的时候,bits中的第1比特将被设置成0。

masks的定义如下:

struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - 
			 SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];

实际上大小是3:

#define	SNDRV_PCM_HW_PARAM_ACCESS	0	/* Access type */
#define	SNDRV_PCM_HW_PARAM_FORMAT	1	/* Format */
#define	SNDRV_PCM_HW_PARAM_SUBFORMAT	2	/* Subformat */

其实masks[0]代表访问类型,其实就是上面看到的mmap相关操作的内容,256bit其实只用了几位。mmap相关的内容,读写文件的时候会去展开。

masks[1]代表的是支持的数据格式,其实它就是snd_pcm_hardware中formats的值,会被支持拷贝过来。

masks[2]只用了一位,SNDRV_PCM_SUBFORMAT_STD。一直被设置成1,没看到有什么具体作用。

2.  snd_interval

struct snd_interval {
	unsigned int min, max;
	unsigned int openmin:1,
		     openmax:1,
		     integer:1,
		     empty:1;
};

用来表示一个区间,最大值最小值,已经开区间闭区间。

snd_pcm_hw_constraints 中snd_interval 也是被定义成了数组,它支持的属性也是固定的,其实就是下面这些内容:

#define	SNDRV_PCM_HW_PARAM_SAMPLE_BITS	8	/* Bits per sample */
#define	SNDRV_PCM_HW_PARAM_FRAME_BITS	9	/* Bits per frame */
#define	SNDRV_PCM_HW_PARAM_CHANNELS	10	/* Channels */
#define	SNDRV_PCM_HW_PARAM_RATE		11	/* Approx rate */
#define	SNDRV_PCM_HW_PARAM_PERIOD_TIME	12	/* Approx distance between
						 * interrupts in us
						 */
#define	SNDRV_PCM_HW_PARAM_PERIOD_SIZE	13	/* Approx frames between
						 * interrupts
						 */
#define	SNDRV_PCM_HW_PARAM_PERIOD_BYTES	14	/* Approx bytes between
						 * interrupts
						 */
#define	SNDRV_PCM_HW_PARAM_PERIODS	15	/* Approx interrupts per
						 * buffer
						 */
#define	SNDRV_PCM_HW_PARAM_BUFFER_TIME	16	/* Approx duration of buffer
						 * in us
						 */
#define	SNDRV_PCM_HW_PARAM_BUFFER_SIZE	17	/* Size of buffer in frames */
#define	SNDRV_PCM_HW_PARAM_BUFFER_BYTES	18	/* Size of buffer in bytes */
#define	SNDRV_PCM_HW_PARAM_TICK_TIME	19	/* Approx tick duration in us */

那一个简单的做例子,一个硬件可以支持单声道,双声道,设置八声道。这里就有了最小值和最大值,所以用snd_interval来表示。

3. snd_pcm_hw_rule

struct snd_pcm_hw_rule {
	unsigned int cond;
	int var;
	int deps[5];

	snd_pcm_hw_rule_func_t func;
	void *private;
};

前面看到可以给硬件设置占位标记或者区间值,这些值本身之间是有联系的。举个简单的例子:

SNDRV_PCM_HW_PARAM_CHANNELS 表示硬件可以支持的声道数;

SNDRV_PCM_HW_PARAM_FRAME_BITS表示一帧数据可以支持的bit数;

SNDRV_PCM_HW_PARAM_SAMPLE_BITS表示一个采样数据占用的bit数。

它们之间有一个关系:

声道数 * 采样bit数 = 帧bit数。

当帧的bit数或者采样数据的bit数发生变化时,可支持的声道数就会发生变化。它们之中的任何一个改变,就需要重新计算声道数。

int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
			int var,
			snd_pcm_hw_rule_func_t func, void *private,
			int dep, ...)

看一下snd_pcm_hw_rule_add的函数声明:runtime是rule要添加到的对象;cond代表条件,什么条件下执行,一般都是0,也就是不需要检测条件;var代表要被改变的参数;func是rule使用时,需要调用的函数;dep可以有4个,代表什么参数改变时会调用该rule。

err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 
            snd_pcm_hw_rule_div, NULL,
            SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);

这个例子中,SNDRV_PCM_HW_PARAM_FRAME_BITS

或者SNDRV_PCM_HW_PARAM_SAMPLE_BITS改变时,需要调用该规则,而改变的值是SNDRV_PCM_HW_PARAM_CHANNELS。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您的问题大致是关于一个设备的读写和控制。我会尽力回答您的问题。 首先,对于第一个问题,您需要通过设备读取出自己学号的后3位。这可以通过设备文件的读操作来实现。您需要打开设备文件并使用read()函数从设备中读取数据,然后从读取的数据中提取出后3位即可。示例代码如下: ```c int fd = open("/dev/device_file", O_RDONLY); // 打开设备文件 char buf[1024]; read(fd, buf, 1024); // 从设备中读取数据 close(fd); // 关闭设备文件 // 从读取的数据中提取出后3位 char last_three_digits[4]; memcpy(last_three_digits, &buf[strlen(buf) - 3], 3); last_three_digits[3] = '\0'; printf("My last three digits of student ID are: %s\n", last_three_digits); ``` 对于第二个问题,设备支持每次写入字符不超过1024个,超过部分被丢弃,并且能够保存最近一次修改前的rwbuf里的内容。这可以通过设备文件的写操作来实现。您需要打开设备文件并使用write()函数向设备中写入数据,然后设备会将最近一次修改前的rwbuf里的内容保存下来。示例代码如下: ```c int fd = open("/dev/device_file", O_WRONLY); // 打开设备文件 char buf[1024] = "Hello, this is a test message."; write(fd, buf, strlen(buf)); // 向设备中写入数据 memset(buf, 0, sizeof(buf)); // 清空缓冲区 read(fd, buf, 1024); // 从设备中读取数据 close(fd); // 关闭设备文件 printf("Read from device: %s\n", buf); // 输出最近一次修改前的内容 ``` 对于第三个问题,设备支持系统调用ioctl(int d, int req,…),共支持设置三种模式:a. 清除设备中写入的字符串; b. 从设备中读时,读出的是最近一次修改前的内容。 c. 从设备中读时,读出的是最新内容。这可以通过ioctl系统调用来实现。示例代码如下: ```c int fd = open("/dev/device_file", O_RDWR); // 打开设备文件 ioctl(fd, CLEAR_DEVICE, NULL); // 清除设备中写入的字符串 char buf[1024]; ioctl(fd, READ_LAST_CONTENT, buf); // 从设备中读取最近一次修改前的内容 printf("Read from device (last content): %s\n", buf); ioctl(fd, READ_NEW_CONTENT, buf); // 从设备中读取最新内容 printf("Read from device (new content): %s\n", buf); close(fd); // 关闭设备文件 ``` 对于第四个问题,设备关闭前不能被多次打开,这可以通过使用一个全局变量来实现。示例代码如下: ```c static int device_open_count = 0; // 全局变量,记录设备打开次数 // 设备文件的open()函数 static int device_open(struct inode *inode, struct file *file) { if (device_open_count > 0) { return -EBUSY; // 设备已经被打开,返回EBUSY错误码 } device_open_count++; // ... return 0; // 打开成功 } // 设备文件的release()函数 static int device_release(struct inode *inode, struct file *file) { device_open_count--; // ... return 0; } ``` 最后,对于第五个问题,您需要编写一个测试程序来验证以上功能。下面是一个简单的测试程序的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #define DEVICE_FILE "/dev/device_file" #define CLEAR_DEVICE 0x01 #define READ_LAST_CONTENT 0x02 #define READ_NEW_CONTENT 0x03 int main() { // 测试读取设备中的学号后3位 int fd = open(DEVICE_FILE, O_RDONLY); char buf[1024]; read(fd, buf, 1024); close(fd); char last_three_digits[4]; memcpy(last_three_digits, &buf[strlen(buf) - 3], 3); last_three_digits[3] = '\0'; printf("My last three digits of student ID are: %s\n", last_three_digits); // 测试向设备中写入数据并读取最近一次修改前的内容 fd = open(DEVICE_FILE, O_WRONLY); char msg[1024] = "Hello, this is a test message."; write(fd, msg, strlen(msg)); memset(buf, 0, sizeof(buf)); ioctl(fd, READ_LAST_CONTENT, buf); printf("Read from device (last content): %s\n", buf); close(fd); // 测试清除设备中的字符串和读取最新内容 fd = open(DEVICE_FILE, O_RDWR); ioctl(fd, CLEAR_DEVICE, NULL); memset(buf, 0, sizeof(buf)); ioctl(fd, READ_NEW_CONTENT, buf); printf("Read from device (new content): %s\n", buf); close(fd); return 0; } ``` 以上就是我对您的问题的回答。希望能够对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值