嵌入式linux下u盘升级的设计

2019-07-12 15:57发布

一.内核配置,配置使其支持u盘make menu_config    Device Drivers --->        [*]USB support -->            <*>   USB Mass Storage supportu盘底层依赖scsi,所以scsi的配置也要配置好二.设计更新代码我是这么设计的:写个应用程序存放在文件系统的/bin目录下,取名update,执行这个程序会遍历 /dev/sd[drive][partition],执行里面定义好的脚本文件,文件名约定为UpDate,脚本文件就可以调用busybox的通用Linux命令,rm,mkdir,cp,touch等命令将u盘上的新二进制或其他文件替换掉旧的文件系统的文件.2.1 update代码[cpp] view plain copy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8.   
  9. unsigned char ch[8]={'a','b','c','d','e','f','g','h'}; //sda,sdb,sdc  
  10. int main(int argc,char **argv)  
  11. {  
  12.     int fd;  
  13.     unsigned char DEV[64];  //u盘等磁盘设备的设备文件路径  
  14.     unsigned char PATH[64]; //Update文件的路径  
  15.     unsigned char cmd[64];  //系统调用的命令  
  16.     int i=0;  
  17.     int j=0;      
  18.   
  19.     for(j=0;j<4;j++){    //最多支持4个分区  
  20.         for(i=0;i<8;i++){    //最多8个硬盘  
  21.             sprintf(PATH,"/media/sd%c%d/UpDate",ch[i],j); //"/media/sda1/UpDate","/media/sda2/UpDate"...  
  22.             sprintf(DEV,"/media/sd%c%d",ch[i],j);  //对应的设备文件路径"/media/sda1","/media/sda2"...  
  23.             fd=open(PATH,O_RDWR);    //打开文件全路径  
  24.             if(fd==-1){    //判断文件是否存在  
  25.                 //printf("can not open '%s' ",PATH);  
  26.                 continue;  //不存在继续扫描下一个分区     
  27.             }  
  28.             else{    //文件存在则跳出子循环  
  29.                 printf("open device '%s' ",PATH);  
  30.                 break;  
  31.             }  
  32.         }  
  33.         if(fd!=-1)    //判断文件是否存在  
  34.             break;   //存在则跳出第二个for循环,不存在则继续下一个磁盘扫描  
  35.     }  
  36.     if(fd==-1){ //判断文件是否存在  
  37.         printf("can not find any u pan!  ");  //这表示所有磁盘所有分区都没有UpDate文件  
  38.     }  
  39.     else{    //文件存在  
  40.         close(fd);  //关闭文件描述符  
  41.         sprintf(cmd,"sh %s %s",PATH,DEV);  //设计执行脚本命令,例如"sh /media/sda1/UpDate /media/sda1"  
  42.         system(cmd);   //执行该脚本  
  43.     }  
  44.     return 0;  
  45. }  
这里cmd将设备文件路径作为第一个参数传递给脚本那么执行的脚本里面就可以通过$1获取DEV(/media/sda1)可以写个简单的脚本测试[cpp] view plain copy
  1. #! /bin/sh  
  2. echo -e "update file!"  
  3. echo $1  
  4. ls -l $1  
  5. touch /opt/1234  
  6. echo -e "update file done!"  

 ok交叉编译应用程序update然后放在/bin下面吧到这里可以在启动代码的执行脚本中执行/bin/update文件来执行u盘中UpDate更新程序了但是事先必须先插上u盘才能在启动过程中执行启动脚本调用到update           --重启自动或者只能插上u盘手工运行update来更新程序.                                                      --手动三.下面来做不用重启的自动,也就是插上u盘自动运行update先测试下u盘插入到识别的原理吧3.1插入u盘打印[cpp] view plain copy
  1. usb 1-1: new high speed USB device using musb-hdrc and address 5  
  2. usb 1-1: New USB device found, idVendor=0951, idProduct=1643  
  3. usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3  
  4. usb 1-1: Product: DataTraveler G3  
  5. usb 1-1: Manufacturer: Kingston  
  6. usb 1-1: SerialNumber: 001CC0EC34F1FB90F71729FF  
  7. scsi5 : usb-storage 1-1:1.0  
  8. scsi 5:0:0:0: Direct-Access     Kingston DataTraveler G3  1.00 PQ: 0 ANSI: 0 CCS  
  9. sd 5:0:0:0: Attached scsi generic sg0 type 0  
  10. sd 5:0:0:0: [sdb] 15644912 512-byte logical blocks: (8.01 GB/7.45 GiB)  
  11. sd 5:0:0:0: [sdb] Write Protect is off  
  12. sd 5:0:0:0: [sdb] Assuming drive cache: write through  
  13. sd 5:0:0:0: [sdb] Assuming drive cache: write through  
  14.  sdb: sdb1  
  15. sd 5:0:0:0: [sdb] Assuming drive cache: write through  
  16. sd 5:0:0:0: [sdb] Attached SCSI removable disk  
  17. FAT: invalid media value (0xb9)  
  18. VFS: Can't find a valid FAT filesystem on dev sdb.  
  19. EXT3-fs (sdb): error: can't find ext3 filesystem on dev sdb.  
  20. EXT2-fs (sdb): error: can't find an ext2 filesystem on dev sdb.  
  21. FAT: invalid media value (0xb9)  
  22. VFS: Can't find a valid FAT filesystem on dev sdb.  
  23. ISOFS: Unable to identify CD-ROM format.  

hub_thread守护线程[khubd]检测到hub状态变化,根hub枚举新的usb设备,获取新的usb设备信息,创建usb_device(usb设备),调用usb_bus_type的match方法,找到对应的usb驱动(usb_driver),这里就是usb_storage_driver.匹配之后调用usb_storage_driver的probe方法storage_probe,接着usb_stor_probe2函数,接着创建一个usb_stor_scan_thread线程来扫描u盘[cpp] view plain copy
  1. usb_stor_scan_thread  
  2.     scsi_scan_host  
  3.         do_scsi_scan_host  
  4.             scsi_scan_host_selected  
  5.                 scsi_scan_channel  
  6.                     __scsi_scan_target  
  7.                         scsi_probe_and_add_lun  
  8.                             scsi_add_lun  
  9.                             scsi_sysfs_add_sdev  

接着调用sisc总线scsi_bus_type的match方法,匹配接着sd_probe,接着sd_probe_async同步接着调用/bin/mount创建设备节点update的扫描是扫描设备节点,mount命令会调用系统调用sys_mount所以我在sys_mount的方法中调用updatesys_mount定义在fs/namespace.c中[cpp] view plain copy
  1. SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,  
  2.         char __user *, type, unsigned long, flags, void __user *, data)  
  3. {  
  4.     int ret;  
  5.     char *kernel_type;  
  6.     char *kernel_dir;  
  7.     char *kernel_dev;  
  8.     unsigned long data_page;  
  9.   
  10.     ret = copy_mount_string(type, &kernel_type);  
  11.     if (ret < 0)  
  12.         goto out_type;  
  13.   
  14.     kernel_dir = getname(dir_name);  
  15.     if (IS_ERR(kernel_dir)) {  
  16.         ret = PTR_ERR(kernel_dir);  
  17.         goto out_dir;  
  18.     }  
  19.   
  20.     ret = copy_mount_string(dev_name, &kernel_dev);  
  21.     if (ret < 0)  
  22.         goto out_dev;  
  23.   
  24.     ret = copy_mount_options(data, &data_page);  
  25.     if (ret < 0)  
  26.         goto out_data;  
  27.   
  28.     ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,  
  29.         (void *) data_page);  
  30.       
  31.     call_usermodehelper ("/bin/update", NULL, NULL, 1); //MHB update ---就加了这句ok  
  32.     free_page(data_page);  
  33. out_data:  
  34.     kfree(kernel_dev);  
  35. out_dev:  
  36.     putname(kernel_dir);  
  37. out_dir:  
  38.     kfree(kernel_type);  
  39. out_type:  
  40.     return ret;  
  41. }  
虽然感觉硬件--应用层--设备驱动--应用程--内核--应用程--shell这样的路子别扭别扭的,但我觉得对我来说是最简单的招吧另一种方法:修改udev规则在/etc/udev/scripts下的mount.sh文件在"add"分支中添加/bin/update
四.简单的补充下u盘驱动的分析1.入口函数[cpp] view plain copy
  1. module_init(usb_stor_init);  
  2. static int __init usb_stor_init(void)  
  3. {  
  4.     int retval;  
  5.       printk("usb --- usb_stor_init start ");  
  6.     retval = usb_register(&usb_storage_driver); //注册u盘设备驱动  
  7.     if (retval == 0)  
  8.             printk("ENE USB Mass Storage support registered. ");  
  9.     return retval;  
  10. }  
2.u盘设备驱动[cpp] view plain copy
  1. static struct usb_driver usb_storage_driver = {  
  2.     .name           =   "usb-storage",          //驱动名  
  3.     .probe          =   storage_probe,          //probe方法(u盘插入)  
  4.     .disconnect     =   usb_stor_disconnect,    //断开方法  
  5.     .suspend        =   usb_stor_suspend,       //挂起  
  6.     .resume         =   usb_stor_resume,        //唤醒  
  7.     .reset_resume   =   usb_stor_reset_resume,  //复位唤醒  
  8.     .pre_reset      =   usb_stor_pre_reset,     //预复位  
  9.     .post_reset     =   usb_stor_post_reset,    //  
  10.     .id_table       =   usb_storage_usb_ids,    //支持id表  
  11.     .supports_autosuspend = 1,  
  12.     .soft_unbind    =   1,  
  13. };  
3.支持设备id表[cpp] view plain copy
  1. struct usb_device_id usb_storage_usb_ids[] = {  
  2. #   include "unusual_devs.h"    //包含头文件unusual_devs.h  
  3.     { }     /* Terminating entry */  
  4. };  
包含了一个unusual_devs.h头文件
该头文件包含了特殊的u盘设备id信息,也包含了通用的u盘设备类信息[cpp] view plain copy
  1. /* Control/Bulk transport for all SubClass values */    //控制/bulk传输子类  
  2. USUAL_DEV(USB_SC_RBC, USB_PR_CB, USB_US_TYPE_STOR),     //典型的flash设备  
  3. USUAL_DEV(USB_SC_8020, USB_PR_CB, USB_US_TYPE_STOR),    //CD-ROM  
  4. USUAL_DEV(USB_SC_QIC, USB_PR_CB, USB_US_TYPE_STOR),     //QIC-157  
  5. USUAL_DEV(USB_SC_UFI, USB_PR_CB, USB_US_TYPE_STOR),     //磁盘  
  6. USUAL_DEV(USB_SC_8070, USB_PR_CB, USB_US_TYPE_STOR),    //可移动媒介  
  7. USUAL_DEV(USB_SC_SCSI, USB_PR_CB, USB_US_TYPE_STOR),    //Transparent  
  8. /* Control/Bulk/Interrupt transport for all SubClass values */  //控制/bulk/中断传输子类  
  9. USUAL_DEV(USB_SC_RBC, USB_PR_CBI, USB_US_TYPE_STOR),  
  10. USUAL_DEV(USB_SC_8020, USB_PR_CBI, USB_US_TYPE_STOR),  
  11. USUAL_DEV(USB_SC_QIC, USB_PR_CBI, USB_US_TYPE_STOR),  
  12. USUAL_DEV(USB_SC_UFI, USB_PR_CBI, USB_US_TYPE_STOR),  
  13. USUAL_DEV(USB_SC_8070, USB_PR_CBI, USB_US_TYPE_STOR),  
  14. USUAL_DEV(USB_SC_SCSI, USB_PR_CBI, USB_US_TYPE_STOR),  
  15. /* Bulk-only transport for all SubClass values */   //bulk传输子类  
  16. USUAL_DEV(USB_SC_RBC, USB_PR_BULK, USB_US_TYPE_STOR),  
  17. USUAL_DEV(USB_SC_8020, USB_PR_BULK, USB_US_TYPE_STOR),  
  18. USUAL_DEV(USB_SC_QIC, USB_PR_BULK, USB_US_TYPE_STOR),  
  19. USUAL_DEV(USB_SC_UFI, USB_PR_BULK, USB_US_TYPE_STOR),  
  20. USUAL_DEV(USB_SC_8070, USB_PR_BULK, USB_US_TYPE_STOR),  
  21. USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0),  
u盘驱动probe方法
1.probe方法storage_probe[cpp] view plain copy
  1. static int storage_probe(struct usb_interface *intf,const struct usb_device_id *id)  
  2. {  
  3.     struct us_data *us;  
  4.     int result;  
  5.   
  6.     if (usb_usual_check_type(id, USB_US_TYPE_STOR) || usb_usual_ignore_device(intf))    //检测u盘设备类型  
  7.         return -ENXIO;  
  8.     /* 
  9.      * Call the general probe procedures. 
  10.      */  
  11.     result = usb_stor_probe1(&us, intf, id,(id - usb_storage_usb_ids) + us_unusual_dev_list);  
  12.     if (result)  
  13.         return result;  
  14.     /* No special transport or protocol settings in the main module */  
  15.     result = usb_stor_probe2(us);  
  16.     return result;  
  17. }  
storage_probe分成了两部分,第一部分由usb_stor_probe1完成通用的配置,第二部分由usb_stor_probe2
2.第一部分probe usb_stor_probe1
[cpp] view plain copy
  1. int usb_stor_probe1(struct us_data **pus,struct usb_interface *intf,const struct usb_device_id *id,struct us_unusual_dev *unusual_dev)  
  2. {  
  3.     struct Scsi_Host *host;  
  4.     struct us_data *us;  
  5.     int result;  
  6.     US_DEBUGP("USB Mass Storage device detected ");  
  7.   
  8.     host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));   //分配Scsi_Host,结构体对象尾部分配us_data对象内存  
  9.     if (!host) {  
  10.         dev_warn(&intf->dev,"Unable to allocate the scsi host ");  
  11.         return -ENOMEM;  
  12.     }  
  13.     host->max_cmd_len = 16;  
  14.     host->sg_tablesize = usb_stor_sg_tablesize(intf);  
  15.     *pus = us = host_to_us(host);   //(struct us_data *) host->hostdata;  
  16.     memset(us, 0, sizeof(struct us_data));  //初始化us_data对象  
  17.     mutex_init(&(us->dev_mutex));  
  18.     init_completion(&us->cmnd_ready);        //初始化completion对象cmnd_ready  
  19.     init_completion(&(us->notify));          //初始化completion对象notify  
  20.     init_waitqueue_head(&us->delay_wait);    //初始化等待队列对象delay_wait  
  21.     init_completion(&us->scanning_done); //初始化completion对象scanning_done  
  22.     result = associate_dev(us, intf);   //捆绑usb设备的一些数据  
  23.     if (result)  
  24.         goto BadDevice;  
  25.     result = get_device_info(us, id, unusual_dev);  //获取unusual_dev入口和描述符  
  26.     if (result)  
  27.         goto BadDevice;  
  28.     get_transport(us);  //设置传输标准  
  29.     get_protocol(us);   //设置协议  
  30.     return 0;  
  31. BadDevice:  
  32.     US_DEBUGP("storage_probe() failed ");  
  33.     release_everything(us);  
  34.     return result;  
  35. }  
  36. EXPORT_SYMBOL_GPL(usb_stor_probe1);  
这里主要是调用了scsi_host_alloc分配了Scsi_host对象2.1 scsi_host_template对象[cpp] view plain copy
  1. struct scsi_host_template usb_stor_host_template = {  
  2.     .name =         "usb-storage",  
  3.     .proc_name =    "usb-storage",  
  4.     .proc_info =    proc_info,  
  5.     .info =         host_info,  
  6.     .queuecommand =     queuecommand,  
  7.     .eh_abort_handler = command_abort,  
  8.     .eh_device_reset_handler =  device_reset,  
  9.     .eh_bus_reset_handler =     bus_reset,  
  10.     .can_queue =    1,  
  11.     .cmd_per_lun =  1,  
  12.     .this_id =      -1,  
  13.     .slave_alloc =      slave_alloc,  
  14.     .slave_configure =  slave_configure,  
  15.     .sg_tablesize =     SCSI_MAX_SG_CHAIN_SEGMENTS,  
  16.     .max_sectors =      240,  
  17.     .use_clustering =   1,  
  18.     .emulated =         1,  
  19.     .skip_settle_delay =    1,  
  20.     .sdev_attrs =   sysfs_device_attr_list,  
  21.     .module =   THIS_MODULE  
  22. };  
在scsi接口底层会调用到它的多个方法3.第二部分probe usb_stor_probe2
[cpp] view plain copy
  1. int usb_stor_probe2(struct us_data *us)  
  2. {  
  3.     struct task_struct *th;  
  4.     int result;  
  5.     struct device *dev = &us->pusb_intf->dev;  
  6.     if (!us->transport || !us->proto_handler) {   //判断传输方式和协议是否设置好  
  7.         result = -ENXIO;  
  8.         goto BadDevice;  
  9.     }  
  10.     US_DEBUGP("Transport: %s ", us->transport_name);  
  11.     US_DEBUGP("Protocol: %s ", us->protocol_name);  
  12.     if (us->fflags & US_FL_SINGLE_LUN)  
  13.         us->max_lun = 0;  
  14.     result = get_pipes(us);  
  15.     if (result)  
  16.         goto BadDevice;  
  17.     result = usb_stor_acquire_resources(us);  
  18.     if (result)  
  19.         goto BadDevice;  
  20.     snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",dev_name(&us->pusb_intf->dev));  
  21.     result = scsi_add_host(us_to_host(us), dev);    //添加Scsi_Host对象  
  22.     if (result) {  
  23.         dev_warn(dev,"Unable to add the scsi host ");  
  24.         goto BadDevice;  
  25.     }  
  26.     th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan"); //创建线程  
  27.     if (IS_ERR(th)) {  
  28.         dev_warn(dev,"Unable to start the device-scanning thread ");  
  29.         complete(&us->scanning_done);  
  30.         quiesce_and_remove_host(us);  
  31.         result = PTR_ERR(th);  
  32.         goto BadDevice;  
  33.     }  
  34.     usb_autopm_get_interface_no_resume(us->pusb_intf);  
  35.     wake_up_process(th);  
  36.     return 0;  
  37. BadDevice:  
  38.     US_DEBUGP("storage_probe() failed ");  
  39.     release_everything(us);  
  40.     return result;  
  41. }  
  42. EXPORT_SYMBOL_GPL(usb_stor_probe2);  
这里添加了Scsi_Host对象并创建了一个线程u盘扫描线程[cpp] view plain copy
  1. static int usb_stor_scan_thread(void * __us)  
  2. {  
  3.     struct us_data *us = (struct us_data *)__us;  
  4.     struct device *dev = &us->pusb_intf->dev;  
  5.     dev_dbg(dev, "device found ");  
  6.     set_freezable();  
  7.     if (delay_use > 0) { //延时等待设备准备好  
  8.         dev_dbg(dev, "waiting for device to settle before scanning ");  
  9.         wait_event_freezable_timeout(us->delay_wait,test_bit(US_FLIDX_DONT_SCAN, &us->dflags),delay_use * HZ);  
  10.     }  
  11.     if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {  
  12.         if (us->protocol == USB_PR_BULK && !(us->fflags & US_FL_SINGLE_LUN)) {  
  13.             mutex_lock(&us->dev_mutex);  
  14.             us->max_lun = usb_stor_Bulk_max_lun(us);  
  15.             mutex_unlock(&us->dev_mutex);  
  16.         }  
  17.         scsi_scan_host(us_to_host(us)); //扫描scsi适配器  
  18.         dev_dbg(dev, "scan complete ");  
  19.     }  
  20.     usb_autopm_put_interface(us->pusb_intf);  
  21.     complete_and_exit(&us->scanning_done, 0);  
  22. }  
在线程里会调用scsi_scan_host函数方法 这里面可以总结出u'盘的probe方法与下一层scsi接口的调用关系[cpp] view plain copy
  1.             --  storage_probe  
  2. scsi_host_alloc     --  usb_stor_probe1  
  3. scsi_add_host       --  usb_stor_probe2  
  4. scsi_scan_host      --  usb_stor_scan_thread线程  
  5.     |  
  6.     slave_alloc()  
  7.     slave_configure()  
在参考scsi的文档中有介绍这种模型[cpp] view plain copy
  1. Hotplug initialization model  
  2. ============================  
  3. In this model an LLD controls when SCSI hosts are introduced and removed  
  4. from the SCSI subsystem. Hosts can be introduced as early as driver  
  5. initialization and removed as late as driver shutdown. Typically a driver  
  6. will respond to a sysfs probe() callback that indicates an HBA has been  
  7. detected. After confirming that the new device is one that the LLD wants  
  8. to control, the LLD will initialize the HBA and then register a new host  
  9. with the SCSI mid level.  
  10.   
  11. During LLD initialization the driver should register itself with the  
  12. appropriate IO bus on which it expects to find HBA(s) (e.g. the PCI bus).  
  13. This can probably be done via sysfs. Any driver parameters (especially  
  14. those that are writable after the driver is loaded) could also be  
  15. registered with sysfs at this point. The SCSI mid level first becomes  
  16. aware of an LLD when that LLD registers its first HBA.  
  17.   
  18. At some later time, the LLD becomes aware of an HBA and what follows  
  19. is a typical sequence of calls between the LLD and the mid level.  
  20. This example shows the mid level scanning the newly introduced HBA for 3   
  21. scsi devices of which only the first 2 respond:  
  22.   
  23.      HBA PROBE: assume 2 SCSI devices found in scan  
  24. LLD                   mid level                    LLD  
  25. ===-------------------=========--------------------===------  
  26. scsi_host_alloc()  -->  
  27. scsi_add_host()  ---->  
  28. scsi_scan_host()  -------+  
  29.                          |  
  30.                     slave_alloc()  
  31.                     slave_configure() -->  scsi_adjust_queue_depth()  
  32.                          |  
  33.                     slave_alloc()  
  34.                     slave_configure()  
  35.                          |  
  36.                     slave_alloc()   ***  
  37.                     slave_destroy() ***  
  38. ------------------------------------------------------------  
  39.   
  40. If the LLD wants to adjust the default queue settings, it can invoke  
  41. scsi_adjust_queue_depth() in its slave_configure() routine.  
  42.   
  43. *** For scsi devices that the mid level tries to scan but do not  
  44.     respond, a slave_alloc(), slave_destroy() pair is called.  
  45.   
  46. When an HBA is being removed it could be as part of an orderly shutdown  
  47. associated with the LLD module being unloaded (e.g. with the "rmmod"  
  48. command) or in response to a "hot unplug" indicated by sysfs()'s  
  49. remove() callback being invoked. In either case, the sequence is the  
  50. same:  
  51.   
  52.         HBA REMOVE: assume 2 SCSI devices attached  
  53. LLD                      mid level                 LLD  
  54. ===----------------------=========-----------------===------  
  55. scsi_remove_host() ---------+  
  56.                             |  
  57.                      slave_destroy()  
  58.                      slave_destroy()  
  59. scsi_host_put()  
  60. ------------------------------------------------------------  
  61.                        
  62. It may be useful for a LLD to keep track of struct Scsi_Host instances  
  63. (a pointer is returned by scsi_host_alloc()). Such instances are "owned"  
  64. by the mid-level.  struct Scsi_Host instances are freed from  
  65. scsi_host_put() when the reference count hits zero.  
  66.   
  67. Hot unplugging an HBA that controls a disk which is processing SCSI  
  68. commands on a mounted file system is an interesting situation. Reference  
  69. counting logic is being introduced into the mid level to cope with many  
  70. of the issues involved. See the section on reference counting below.  
  71.   
  72.   
  73. The hotplug concept may be extended to SCSI devices. Currently, when an  
  74. HBA is added, the scsi_scan_host() function causes a scan for SCSI devices  
  75. attached to the HBA's SCSI transport. On newer SCSI transports the HBA  
  76. may become aware of a new SCSI device _after_ the scan has completed.  
  77. An LLD can use this sequence to make the mid level aware of a SCSI device:  
  78.   
  79.                  SCSI DEVICE hotplug  
  80. LLD                   mid level                    LLD  
  81. ===-------------------=========--------------------===------  
  82. scsi_add_device()  ------+  
  83.                          |  
  84.                     slave_alloc()  
  85.                     slave_configure()   [--> scsi_adjust_queue_depth()]  
  86. ------------------------------------------------------------  
  87.   
  88. In a similar fashion, an LLD may become aware that a SCSI device has been  
  89. removed (unplugged) or the connection to it has been interrupted. Some  
  90. existing SCSI transports (e.g. SPI) may not become aware that a SCSI  
  91. device has been removed until a subsequent SCSI command fails which will  
  92. probably cause that device to be set offline by the mid level. An LLD that  
  93. detects the removal of a SCSI device can instigate its removal from  
  94. upper layers with this sequence:  
  95.   
  96.                   SCSI DEVICE hot unplug  
  97. LLD                      mid level                 LLD  
  98. ===----------------------=========-----------------===------  
  99. scsi_remove_device() -------+  
  100.                             |  
  101.                      slave_destroy()  
  102. ------------------------------------------------------------  
  103.   
  104. It may be useful for an LLD to keep track of struct scsi_device instances  
  105. (a pointer is passed as the parameter to slave_alloc() and  
  106. slave_configure() callbacks). Such instances are "owned" by the mid-level.  
  107. struct scsi_device instances are freed after slave_destroy().