微视linux scsi驱动错误中断处理

博客介绍了SCSI两种常见故障类型,一是卡自身故障发出错误中断或中断自带错误信息,二是卡无响应丢中断触发定时器超时错误。还提及第一种故障模型中硬件中断执行结束后触发软中断及错误处理线程流程。

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

SCSI有两种常见的故障类型。
一种是卡自身故障发出错误中断或者中断里面自带错误信息;
另外一种是卡没有响应,丢中断触发定时器超时错误。

对于第一种故障模型:
硬件中断执行结束后会触发软中断,流程如下

void blk_done_softirq(struct softirq_action *h)
{
	struct list_head *cpu_list, local_list;

	local_irq_disable();
	cpu_list = &__get_cpu_var(blk_cpu_done);
	list_replace_init(cpu_list, &local_list);
	local_irq_enable();

	while (!list_empty(&local_list)) {//遍历链表,执行钩子函数
		struct request *rq;

		rq = list_entry(local_list.next, struct request, csd.list);
		list_del_init(&rq->csd.list);
		rq->q->softirq_done_fn(rq);
		=>void scsi_softirq_done(struct request *rq)
		{
			/*解析底层控制器中断的处理结果,对于USB控制器,是由usb_stor_invoke_transport完成
			 *错误一般是重试,走NEEDS_RETRY分支,最多重试5次,超过5次走default分支
			 */
			disposition = scsi_decide_disposition(cmd);
			
			switch (disposition) {
				case SUCCESS:
					scsi_finish_command(cmd);
					break;
				case NEEDS_RETRY:
					scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY);
					break;
				case ADD_TO_MLQUEUE:
					scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
					break;
				default:
					ret = !scsi_eh_scmd_add(cmd, 0);
					=>int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
					{
						struct Scsi_Host *shost = scmd->device->host;
						unsigned long flags;
						int ret = 0;

						if (!shost->ehandler)
							return 0;

						spin_lock_irqsave(shost->host_lock, flags);
						if (scsi_host_set_state(shost, SHOST_RECOVERY))
							if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY))
								goto out_unlock;

						ret = 1;
						scmd->eh_eflags |= eh_flag;
						list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
						shost->host_failed++;
						scsi_eh_wakeup(shost);//唤醒异常处理线程
						void scsi_eh_wakeup(struct Scsi_Host *shost)//内容太多,详见下面
						
					 out_unlock:
						spin_unlock_irqrestore(shost->host_lock, flags);
						return ret;
					}

					
					if (ret)
						scsi_finish_command(cmd);
			}
		}
	}
		
}

错误处理线程流程如下:

void scsi_eh_wakeup(struct Scsi_Host *shost)
{
	if (shost->host_busy == shost->host_failed) 
		wake_up_process(shost->ehandler);
		=>int scsi_error_handler(void *data)
		{
			struct Scsi_Host *shost = data;

			/*
			 * We use TASK_INTERRUPTIBLE so that the thread is not
			 * counted against the load average as a running process.
			 * We never actually get interrupted because kthread_run
			 * disables signal delivery for the created thread.
			 */
			set_current_state(TASK_INTERRUPTIBLE);
			while (!kthread_should_stop()) {
				if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) ||
					shost->host_failed != shost->host_busy) {
					schedule();
					set_current_state(TASK_INTERRUPTIBLE);
					continue;
				}

				__set_current_state(TASK_RUNNING);

				/*
				 * We have a host that is failing for some reason.  Figure out
				 * what we need to do to get it up and online again (if we can).
				 * If we fail, we end up taking the thing offline.
				 */
				if (shost->transportt->eh_strategy_handler)//如果有自定义的钩子函数则执行自定义钩子函数
					shost->transportt->eh_strategy_handler(shost);
				else
					scsi_unjam_host(shost);//系统默认钩子函数
					=>void scsi_unjam_host(struct Scsi_Host *shost)
					{
						unsigned long flags;
						LIST_HEAD(eh_work_q);
						LIST_HEAD(eh_done_q);

						spin_lock_irqsave(shost->host_lock, flags);
						list_splice_init(&shost->eh_cmd_q, &eh_work_q);
						spin_unlock_irqrestore(shost->host_lock, flags);

						SCSI_LOG_ERROR_RECOVERY(1, scsi_eh_prt_fail_stats(shost, &eh_work_q));

						if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))
							if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))
								/*Scsiglue.c (drivers\usb\storage):	.eh_abort_handler =		command_abort,*/
								=>int command_abort(struct scsi_cmnd *srb)
								{
									set_bit(US_FLIDX_TIMED_OUT, &us->dflags);//定时器唤醒
									if (!test_bit(US_FLIDX_RESETTING, &us->dflags)) {
										set_bit(US_FLIDX_ABORTING, &us->dflags);
										usb_stor_stop_transport(us);
										/* Stop the current URB transfer */
										=>void usb_stor_stop_transport(struct us_data *us)
										{
											if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {
												US_DEBUGP("-- cancelling URB\n");
												usb_unlink_urb(us->current_urb);
												=>int usb_unlink_urb(struct urb *urb)
												{
													return usb_hcd_unlink_urb(urb, -ECONNRESET);
													=>int usb_hcd_unlink_urb (struct urb *urb, int status)
													{
														retval = unlink1(hcd, urb, status);
														=>int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)
														{
															value = usb_rh_urb_dequeue(hcd, urb, status);
															=>int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
															{
																usb_hcd_giveback_urb(hcd, urb, status);
																=>void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
																{
																	urb->status = status;
																	urb->complete (urb);
																}
															}
														}
													}
												}
											}
										}
									}
								}
							
								scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);
								=>void scsi_eh_ready_devs(struct Scsi_Host *shost,
											struct list_head *work_q,
											struct list_head *done_q)
								{
									if (!scsi_eh_stu(shost, work_q, done_q))//逐级从轻到重复位
										if (!scsi_eh_bus_device_reset(shost, work_q, done_q))
											if (!scsi_eh_target_reset(shost, work_q, done_q))
												if (!scsi_eh_bus_reset(shost, work_q, done_q))
													if (!scsi_eh_host_reset(work_q, done_q))
														/*搞不定则将其踢出去*/
														scsi_eh_offline_sdevs(work_q, done_q);
								}

						scsi_eh_flush_done_q(&eh_done_q);
					}

				/*
				 * Note - if the above fails completely, the action is to take
				 * individual devices offline and flush the queue of any
				 * outstanding requests that may have been pending.  When we
				 * restart, we restart any I/O to any other devices on the bus
				 * which are still online.
				 */
				scsi_restart_operations(shost);
				set_current_state(TASK_INTERRUPTIBLE);
			}
			__set_current_state(TASK_RUNNING);

			shost->ehandler = NULL;
			return 0;
		}
}

 

### 使用Python实现微视工业相机SDK的接口调用和图像采集 对于希望利用Python来操作微视工业相机的应用开发者来说,通常面临的主要挑战在于如何有效地桥接Python环境与原生C/C++ SDK之间的交互。鉴于大多数硬件制造商提供的SDK是以C或C++形式发布的,这要求开发者采取特定策略以确保两种编程语言间的无缝协作。 一种常见做法是在C/C++层面上创建一个中间件,该中间件能够被SWIG或其他类似的工具转换成可供Python使用的模块[^2]。此过程涉及编写必要的包装函数以便于暴露给Python的关键功能点,比如初始化设备、配置参数设置以及数据捕获等核心能力。一旦完成了这一阶段的工作,则可以在Python端导入生成好的库,并按照文档指导完成进一步的操作流程定义。 具体到微视工业相机SDK上,在成功构建了上述桥梁之后,可以通过如下方式执行基本的任务: #### 初始化连接 建立同摄像机通信的第一步通常是加载相应的驱动程序并启动会话。假设已经拥有了经过适当封装后的API访问权限,那么这部分工作可能看起来像这样: ```python import mvCameraControl as mvcam # 假设这是由C/C++编译得到并与Python兼容的模块名 camera = mvcam.Camera() # 创建一个新的相机实例对象 ret = camera.MV_CC_CreateHandle(None, "cam0") # 尝试打开指定ID编号为'cam0'的物理设备句柄 if ret != mvcam.SUCCESS: raise Exception(f"Failed to create handle with error code {ret}") ``` #### 设置属性 为了获取高质量的画面输出,往往还需要调整一些预设选项,例如分辨率、帧率或是曝光时间等等。这类设定一般都对应着具体的寄存器地址或者枚举类型的值,所以务必参照官方手册仔细核对每一个细节。 ```python # 设定目标宽度和高度 (单位像素) desired_width = 1920 desired_height = 1080 camera.MV_CC_SetEnumValue("Width", desired_width) camera.MV_CC_SetEnumValue("Height", desired_height) # 调整自动增益控制状态 auto_gain_state = True camera.MV_CC_SetBoolValue("GainAuto", auto_gain_state) ``` #### 开始抓取图片 当所有的前期准备工作完成后,就可以正式进入图像捕捉环节了。这里需要注意的是,实际应用中可能会涉及到多线程处理机制以提高效率;而对于简单的场景而言,同步模式下的单次读取也完全可以满足需求。 ```python def grab_one_image(camera_instance): buffer_size = ... # 计算所需缓冲区大小 image_buffer = bytearray(buffer_size) status = camera_instance.MV_CC_GetImageBuffer(image_buffer, ...) if status == mvcam.SUCCESS: process_image_data(image_buffer) # 对获得的数据做下一步解析/保存等工作 else: log_error(status) # 处理错误情况 grab_one_image(camera) ``` 以上仅作为概念性的介绍框架,真实情况下还需依据具体情况做出相应修改优化。另外值得注意的一点是,不同品牌型号之间存在差异性,因此建议始终优先参考所使用产品的最新版本技术指南来进行开发活动。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值