linux kernel将buf保存到文件做法 很好的调试方法

本文介绍了两种在Linux内核中记录关键信息到文件的方法。一种是保存机器启动过程中的VBATT电压值;另一种是接收并保存UART串口数据为十六进制格式。通过示例代码展示了如何使用内核API实现文件的打开、写入及关闭。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux kernel将关键信息保存到文件做法      很好的调试方法
下面有2个示例:
    1:保存机器从开机到结束的VBATT;
    2:保存uart接收到的数据到文件;


意义不多说了。

以下是代码:

#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>

static  struct file *fp =NULL;

int  write_to_file (char *buf, int size)
{
     int ret = 0;
     struct file *fp;
     mm_segment_t old_fs;
     loff_t pos = 0;
     int nwrite = 0;
     static int offset = 0;
     static int first_flag=0;

     /* change to KERNEL_DS address limit */
     old_fs = get_fs();
     set_fs(KERNEL_DS);


     if( first_flag==0){
     first_flag=1;
     /* open file to write */
     fp = filp_open("/data/at_log1", O_WRONLY|O_CREAT, 0640);
     if (!fp) {
         printk("%s: open file error\n", __FUNCTION__);
         ret = -1;
         goto exit;
         }
     }
     pos=(unsigned long)offset;

     /* Write buf to file */
     nwrite=vfs_write(fp, buf, size, &pos);
     offset +=nwrite;

exit:

     return ret;
}



int xxxx_exit(void){
if (fp)
          filp_close(fp, NULL);
}




1:保存VBATT与SOC:

static int adjust_soc(struct pm8921_bms_chip *chip, int soc,
        int batt_temp, int chargecycles,
        int rbatt, int fcc_uah, int uuc_uah, int cc_uah)
{
    ------------
   static char vbatt[100];
    rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(
                            &ibat_ua,
                            &vbat_uv);
    if (rc < 0) {
        pr_err("simultaneous vbat ibat failed err = %d\n", rc);
        goto out;
    }

    chip->vbat_mv= vbat_uv/1000;
    sprintf(vbatt,"%d        %d\n",the_chip->vbat_mv,calculated_soc);
    write_to_file(vbatt,strlen(vbatt));
    ----------
}

2:将串口接收到的字符转化为16进制保存到文件中:

static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr)
{
  
    ---------------
    static char temp[1024];

    rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);

    /* order the read of rx.buffer */
    rmb();

    if (0 != (uport->read_status_mask & CREAD)) {
        retval = tty_insert_flip_string(tty, msm_uport->rx.buffer,
                        rx_count);
        if (retval != rx_count) {
            msm_uport->rx.buffer_pending |= CHARS_NORMAL |
                retval << 5 | (rx_count - retval) << 16;
        }
        if(rx_count<=512){
            //memcpy(temp,msm_uport->rx.buffer,rx_count);
            for(i=0;i<rx_count;i++){
            sprintf(temp+2*i,"%02x",msm_uport->rx.buffer[i]);
            write_to_file(temp,strlen(temp));
            }
                
        }
          ----------------
}



<think>好的,我现在需要帮助用户深入分析Linux Kernel 4.4及以上版本中Mass Storage Device(MSD)侧的代码实现原理和详细结构。用户之前的问题是关于在内核空间使用filp_open和相关读写函数的,现在转向了USB Mass Storage设备的代码解析,特别是设备侧(gadget)的实现。 首先,我需要回顾用户提供的引用内容。引用[2]提到了USB gadget的configfs初始化代码,引用[3]涉及Mass Storage设备在错误处理时的行为。虽然这些引用可能不完全符合用户当前的需求,但它们提示了USB gadget和Mass Storage模块的相关性。用户要求的是Kernel 4.4+版本的MSD设备侧代码分析,因此我需要聚焦于该版本之后的代码结构。 接下来,我需要确定Linux内核中Mass Storage设备侧的主要代码位置。通常,USB gadget驱动位于drivers/usb/gadget/目录下,而Mass Storage的具体实现可能在storage_common.c、f_mass_storage.c等文件中。同时,configfs的集成部分也很重要,因为现代内核倾向于使用configfs配置USB功能模块。 然后,我应该考虑代码的分层结构。USB gadget架构通常分为三层:控制器驱动(UDC)、Gadget Function API和具体功能实现(如Mass Storage)。Mass Storage模块需要处理SCSI命令、Bulk-Only Transport协议以及存储介质的访问。 还需要分析初始化流程,包括模块的注册、configfs条目的创建,以及如何将存储后端(如文件或块设备)与Mass Storage功能绑定。此外,数据传输路径,包括端点配置、请求队列的处理,以及错误恢复机制也是重点。 用户可能对代码中的关键数据结构感兴趣,比如struct fsg_common、struct fsg_lun,以及它们如何管理逻辑单元(LUN)。另外,中断处理和协议状态机的实现细节也需要解释。 最后,我应该确保回答覆盖用户可能关心的性能优化、调试方法以及常见问题排查,比如如何处理SCSI命令错误或DMA问题。同时,结合用户之前的问题,可能需要提到内核空间文件操作在Mass Storage中的应用,比如通过虚拟文件暴露存储内容。</think>#### 一、Mass Storage设备侧架构总览 Linux内核中USB Mass Storage Gadget实现基于**复合设备驱动框架**,核心代码位于: ``` drivers/usb/gadget/function/f_mass_storage.c drivers/usb/gadget/function/storage_common.c ``` 其架构分为三个层级: 1. **控制器驱动层**:处理USB物理端点(EP)操作,如`dwc3`/`udc`驱动 2. **Gadget Function API**:实现USB设备描述符配置和协议栈 3. **Mass Storage协议层**:实现Bulk-Only Transport(BOT)协议和SCSI命令转换[^3] --- #### 二、核心数据结构解析 ##### 1. 逻辑单元描述符(`struct fsg_lun`) ```c struct fsg_lun { struct file *filp; // 关联的存储介质文件 struct block_device *blkdev; // 块设备指针 unsigned int nofua:1; // 是否禁用FUA标志 atomic_t open_count; // 打开计数器 loff_t file_length; // 文件总大小 struct kref ref; // 引用计数 }; ``` 每个LUN对应一个存储单元,支持文件/块设备两种后端[^2] ##### 2. 全局状态机(`struct fsg_common`) ```c struct fsg_common { struct usb_function func; // USB功能描述 struct fsg_lun *luns[FSG_MAX_LUNS]; // 最大支持LUN数 struct usb_ep *ep0; // 控制端点 struct usb_request *ep0req; // 控制请求对象 enum fsg_state state; // 当前状态(IDLE/TRANSPORT等) struct mutex lock; // 状态机锁 }; ``` 管理BOT协议状态转换和SCSI命令处理[^3] --- #### 三、初始化流程深度分析 ##### 1. 模块注册(以configfs为例) ```c // drivers/usb/gadget/legacy/storage_common.c static struct usb_function_instance *mass_storage_alloc_inst(void) { struct fsg_opts *opts; opts = kzalloc(sizeof(*opts), GFP_KERNEL); config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type); return &opts->func_inst; } // drivers/usb/gadget/function/f_mass_storage.c DECLARE_USB_FUNCTION_INIT(mass_storage, mass_storage_alloc_inst, mass_storage_alloc); ``` 通过`USB_FUNCTION_INIT`宏完成Gadget Function注册 ##### 2. LUN配置过程 ```c static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) { struct file *filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); if (IS_ERR(filp)) return PTR_ERR(filp); curlun->filp = filp; curlun->file_length = i_size_read(file_inode(filp)); return 0; } ``` 使用`filp_open`打开存储后端文件(与用户之前研究的kernel文件操作直接相关)[^1] --- #### 四、Bulk-Only Transport协议实现 ##### 1. 数据传输状态机 ```mermaid stateDiagram [*] --> CBW: 接收命令块 CBW --> DATA_OUT: 需要发送数据 CBW --> DATA_IN: 需要接收数据 DATA_OUT --> CSW: 传输完成 DATA_IN --> CSW: 传输完成 CSW --> CBW: 准备下个命令 ``` ##### 2. SCSI命令处理核心 ```c static int do_scsi_command(struct fsg_common *common) { struct fsg_buffhd *bh = common->next_buffhd_to_fill; struct scsi_cmnd *cmnd = (struct scsi_cmnd *)bh->buf; switch (cmnd->cmnd[0]) { case SC_TEST_UNIT_READY: return check_media_state(common); case SC_READ_10: return handle_read(common, cmnd); case SC_WRITE_10: return handle_write(common, cmnd); // ...其他命令处理 } } ``` 实现SCSI命令到存储操作的转换 --- #### 五、关键性能优化点 1. **DMA缓冲区管理** ```c struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC); req->buf = kmalloc(length, GFP_ATOMIC | GFP_DMA); req->dma = dma_map_single(ep->dma, req->buf, length, DMA_BIDIRECTIONAL); return req; } ``` 使用DMA映射提升数据传输效率 2. **请求队列批处理** ```c static void start_transfer(struct fsg_common *common, struct usb_ep *ep, struct usb_request *req, int *pipelined) { if (usb_ep_queue(ep, req, GFP_ATOMIC) == 0) *pipelined = 1; } ``` 通过原子上下队列提交实现零拷贝传输 --- #### 六、调试与问题定位 ##### 1. 启用调试日志 ```c // 修改f_mass_storage.c #define VERBOSE_DEBUG static int dump_cdb = 1; // 打印所有SCSI命令 ``` ##### 2. 使用sysfs接口 ```bash # 查看LUN状态 cat /sys/kernel/debug/usb_gadget/msd/lun0/file # 强制卸载存储介质 echo 0 > /sys/kernel/debug/usb_gadget/msd/lun0/ro ``` --- #### 七、典型问题解决方案 **问题现象**:主机端报告`Phase Error` **排查步骤**: 1. 检查`fsg_common->state`状态机是否卡在异常状态 2. 确认SCSI命令处理函数是否返回正确状态码 3. 验证DMA缓冲区对齐是否符合控制器要求[^3] **修复示例**: ```diff - req->buf = kmalloc(len, GFP_ATOMIC); + req->buf = kmalloc(len, GFP_ATOMIC | __GFP_DMA); ``` --- #### 八、版本演进关键变更(4.4→5.x) 1. **多LUN热插拔支持**(4.15+) ```c configfs_add_lun() // 动态添加LUN ``` 2. **异步IO优化**(5.8+) 使用`io_uring`替代传统bio请求 3. **安全增强**(5.10+) 增加SCSI命令白名单机制 --- --相关问题-- 1. 如何在Mass Storage Gadget中实现多LUN动态配置? 2. Linux USB Gadget框架如何处理端点(Endpoint)资源竞争? 3. 如何通过内核模块参数调整Mass Storage的性能参数?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值