1. 程式人生 > >嵌入式linux下u盤升級的設計

嵌入式linux下u盤升級的設計

一.核心配置,配置使其支援u盤

make menu_config

    Device Drivers --->

        [*]USB support -->

            <*>   USB Mass Storage support

u盤底層依賴scsi,所以scsi的配置也要配置好

二.設計更新程式碼

我是這麼設計的:寫個應用程式存放在檔案系統的/bin目錄下,取名update,執行這個程式會遍歷 /dev/sd[drive][partition],

執行裡面定義好的指令碼檔案,檔名約定為UpDate,指令碼檔案就可以呼叫busybox的通用Linux命令,rm,mkdir,cp,touch等命令

將u盤上的新二進位制或其他檔案替換掉舊的檔案系統的檔案.

2.1 update程式碼

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. unsigned char ch[8]={'a','b','c','d','e','f','g','h'}; //sda,sdb,sdc
  9. int main(int argc,
    char **argv)  
  10. {  
  11.     int fd;  
  12.     unsigned char DEV[64];  //u盤等磁碟裝置的裝置檔案路徑
  13.     unsigned char PATH[64]; //Update檔案的路徑
  14.     unsigned char cmd[64];  //系統呼叫的命令
  15.     int i=0;  
  16.     int j=0;      
  17.     for(j=0;j<4;j++){    //最多支援4個分割槽
  18.         for(i=0;i<8;i++){    //最多8個硬碟
  19.             sprintf(PATH,"/media/sd%c%d/UpDate",ch[i],j); 
    //"/media/sda1/UpDate","/media/sda2/UpDate"...
  20.             sprintf(DEV,"/media/sd%c%d",ch[i],j);  //對應的裝置檔案路徑"/media/sda1","/media/sda2"...
  21.             fd=open(PATH,O_RDWR);    //開啟檔案全路徑
  22.             if(fd==-1){    //判斷檔案是否存在
  23.                 //printf("can not open '%s'\n",PATH);
  24.                 continue;  //不存在繼續掃描下一個分割槽   
  25.             }  
  26.             else{    //檔案存在則跳出子迴圈
  27.                 printf("open device '%s'\n",PATH);  
  28.                 break;  
  29.             }  
  30.         }  
  31.         if(fd!=-1)    //判斷檔案是否存在
  32.             break;   //存在則跳出第二個for迴圈,不存在則繼續下一個磁碟掃描
  33.     }  
  34.     if(fd==-1){ //判斷檔案是否存在
  35.         printf("can not find any u pan!\n ");  //這表示所有磁碟所有分割槽都沒有UpDate檔案
  36.     }  
  37.     else{    //檔案存在
  38.         close(fd);  //關閉檔案描述符
  39.         sprintf(cmd,"sh %s %s",PATH,DEV);  //設計執行指令碼命令,例如"sh /media/sda1/UpDate /media/sda1"
  40.         system(cmd);   //執行該指令碼
  41.     }  
  42.     return 0;  
  43. }  

這裡cmd將裝置檔案路徑作為第一個引數傳遞給指令碼

那麼執行的腳本里面就可以通過$1獲取DEV(/media/sda1)

可以寫個簡單的指令碼測試

  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盤列印

  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盤

  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的方法中呼叫update

sys_mount定義在fs/namespace.c中

  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.     ret = copy_mount_string(type, &kernel_type);  
  10.     if (ret < 0)  
  11.         goto out_type;  
  12.     kernel_dir = getname(dir_name);  
  13.     if (IS_ERR(kernel_dir)) {  
  14.         ret = PTR_ERR(kernel_dir);  
  15.         goto out_dir;  
  16.     }  
  17.     ret = copy_mount_string(dev_name, &kernel_dev);  
  18.     if (ret < 0)  
  19.         goto out_dev;  
  20.     ret = copy_mount_options(data, &data_page);  
  21.     if (ret < 0)  
  22.         goto out_data;  
  23.     ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,  
  24.         (void *) data_page);  
  25.     call_usermodehelper ("/bin/update", NULL, NULL, 1); //MHB update ---就加了這句ok
  26.     free_page(data_page);  
  27. out_data:  
  28.     kfree(kernel_dev);  
  29. out_dev:  
  30.     putname(kernel_dir);  
  31. out_dir:  
  32.     kfree(kernel_type);  
  33. out_type:  
  34.     return ret;  
  35. }  

雖然感覺硬體--應用層--裝置驅動--應用程--核心--應用程--shell這樣的路子彆扭彆扭的,但我覺得對我來說是最簡單的招吧

另一種方法:修改udev規則在/etc/udev/scripts下的mount.sh檔案在"add"分支中新增/bin/update
四.簡單的補充下u盤驅動的分析

1.入口函式

  1. module_init(usb_stor_init);  
  2. staticint __init usb_stor_init(void)  
  3. {  
  4.     int retval;  
  5.       printk("usb --- usb_stor_init start\n");  
  6.     retval = usb_register(&usb_storage_driver); //註冊u盤裝置驅動
  7.     if (retval == 0)  
  8.             printk("ENE USB Mass Storage support registered.\n");  
  9.     return retval;  
  10. }  

2.u盤裝置驅動

  1. staticstruct 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表

  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盤裝置類資訊

  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

  1. staticint storage_probe(struct usb_interface *intf,conststruct usb_device_id *id)  
  2. {  
  3.     struct us_data *us;  
  4.     int result;  
  5.     if (usb_usual_check_type(id, USB_US_TYPE_STOR) || usb_usual_ignore_device(intf))    //檢測u盤裝置型別
  6.         return -ENXIO;  
  7.     /* 
  8.      * Call the general probe procedures. 
  9.      */
  10.     result = usb_stor_probe1(&us, intf, id,(id - usb_storage_usb_ids) + us_unusual_dev_list);  
  11.     if (result)  
  12.         return result;  
  13.     /* No special transport or protocol settings in the main module */
  14.     result = usb_stor_probe2(us);  
  15.     return result;  
  16. }  

storage_probe分成了兩部分,第一部分由usb_stor_probe1完成通用的配置,第二部分由usb_stor_probe2
2.第一部分probe usb_stor_probe1

  1. int usb_stor_probe1(struct us_data **pus,struct usb_interface *intf,conststruct 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\n");  
  7.     host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));   //分配Scsi_Host,結構體物件尾部分配us_data物件記憶體
  8.     if (!host) {  
  9.         dev_warn(&intf->dev,"Unable to allocate the scsi host\n");  
  10.         return -ENOMEM;  
  11.     }  
  12.     host->max_cmd_len = 16;  
  13.     host->sg_tablesize = usb_stor_sg_tablesize(intf);  
  14.     *pus = us = host_to_us(host);   //(struct us_data *) host->hostdata;
  15.     memset(us, 0, sizeof(struct us_data));  //初始化us_data物件
  16.     mutex_init(&(us->dev_mutex));  
  17.     init_completion(&us->cmnd_ready);        //初始化completion物件cmnd_ready
  18.     init_completion(&(us->notify));          //初始化completion物件notify
  19.     init_waitqueue_head(&us->delay_wait);    //初始化等待佇列物件delay_wait
  20.     init_completion(&us->scanning_done); //初始化completion物件scanning_done
  21.     result = associate_dev(us, intf);   //捆綁usb裝置的一些資料
  22.     if (result)  
  23.         goto BadDevice;  
  24.     result = get_device_info(us, id, unusual_dev);  //獲取unusual_dev入口和描述符
  25.     if (result)  
  26.         goto BadDevice;  
  27.     get_transport(us);  //設定傳輸標準
  28.     get_protocol(us);   //設定協議
  29.     return 0;  
  30. BadDevice:  
  31.     US_DEBUGP("storage_probe() failed\n");  
  32.     release_everything(us);  
  33.     return result;  
  34. }  
  35. EXPORT_SYMBOL_GPL(usb_stor_probe1);  

這裡主要是呼叫了scsi_host_alloc分配了Scsi_host物件

2.1 scsi_host_template物件

  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

  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\n", us->transport_name);  
  11.     US_DEBUGP("Protocol: %s\n", 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\n");  
  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\n");  
  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\n");  
  39.     release_everything(us);  
  40.     return result;  
  41. }  
  42. EXPORT_SYMBOL_GPL(usb_stor_probe2);  

這裡添加了Scsi_Host物件並建立了一個執行緒

u盤掃描執行緒

  1. staticint 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\n");  
  6.     set_freezable();  
  7.     if (delay_use > 0) { //延時等待裝置準備好
  8.         dev_dbg(dev, "waiting for device to settle before scanning\n");  
  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\n");  
  19.     }  
  20.     usb_autopm_put_interface(us->pusb_intf);  
  21.     complete_and_exit(&us->scanning_done, 0);  
  22. }  

線上程裡會呼叫scsi_scan_host函式方法

這裡面可以總結出u'盤的probe方法與下一層scsi介面的呼叫關係

  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的文件中有介紹這種模型

  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. During LLD initialization the driver should register itself with the  
  11. appropriate IO bus on which it expects to find HBA(s) (e.g. the PCI bus).  
  12. This can probably be done via sysfs. Any driver parameters (especially  
  13. those that are writable after the driver is loaded) could also be  
  14. registered with sysfs at this point. The SCSI mid level first becomes  
  15. aware of an LLD when that LLD registers its first HBA.  
  16. At some later time, the LLD becomes aware of an HBA and what follows  
  17. is a typical sequence of calls between the LLD and the mid level.  
  18. This example shows the mid level scanning the newly introduced HBA for 3   
  19. scsi devices of which only the first 2 respond:  
  20.      HBA PROBE: assume 2 SCSI devices found in scan  
  21. LLD                   mid level                    LLD  
  22. ===-------------------=========--------------------===------  
  23. scsi_host_alloc()  -->  
  24. scsi_add_host()  ---->  
  25. scsi_scan_host()  -------+  
  26.                          |  
  27.                     slave_alloc()  
  28.                     slave_configure() -->  scsi_adjust_queue_depth()  
  29.                          |  
  30.                     slave_alloc()  
  31.                     slave_configure()  
  32.                          |  
  33.                     slave_alloc()   ***  
  34.                     slave_destroy() ***  
  35. ------------------------------------------------------------  
  36. If the LLD wants to adjust the default queue settings, it can invoke  
  37. scsi_adjust_queue_depth() in its slave_configure() routine.  
  38. *** For scsi devices that the mid level tries to scan but do not  
  39.     respond, a slave_alloc(), slave_destroy() pair is called.  
  40. When an HBA is being removed it could be as part of an orderly shutdown  
  41. associated with the LLD module being unloaded (e.g. with the "rmmod"
  42. command) or in response to a "hot unplug" indicated by sysfs()'s  
  43. remove() callback being invoked. In either case, the sequence is the  
  44. same:  
  45.         HBA REMOVE: assume 2 SCSI devices attached  
  46. LLD                      mid level                 LLD  
  47. ===----------------------=========-----------------===------  
  48. scsi_remove_host() ---------+  
  49.                             |  
  50.                      slave_destroy()  
  51.                      slave_destroy()  
  52. scsi_host_put()  
  53. ------------------------------------------------------------  
  54. It may be useful for a LLD to keep track of struct Scsi_Host instances  
  55. (a pointer is returned by scsi_host_alloc()). Such instances are "owned"
  56. by the mid-level.  struct Scsi_Host instances are freed from  
  57. scsi_host_put() when the reference count hits zero.  
  58. Hot unplugging an HBA that controls a disk which is processing SCSI  
  59. commands on a mounted file system is an interesting situation. Reference  
  60. counting logic is being introduced into the mid level to cope with many  
  61. of the issues involved. See the section on reference counting below.  
  62. The hotplug concept may be extended to SCSI devices. Currently, when an  
  63. HBA is added, the scsi_scan_host() function causes a scan for SCSI devices  
  64. attached to the HBA's SCSI transport. On newer SCSI transports the HBA  
  65. may become aware of a new SCSI device _after_ the scan has completed.  
  66. An LLD can use this sequence to make the mid level aware of a SCSI device:  
  67.                  SCSI DEVICE hotplug  
  68. LLD                   mid level                    LLD  
  69. ===-------------------=========--------------------===------  
  70. scsi_add_device()  ------+  
  71.                          |  
  72.                     slave_alloc()  
  73.                     slave_configure()   [--> scsi_adjust_queue_depth()]  
  74. ------------------------------------------------------------  
  75. In a similar fashion, an LLD may become aware that a SCSI device has been  
  76. removed (unplugged) or the connection to it has been interrupted. Some  
  77. existing SCSI transports (e.g. SPI) may not become aware that a SCSI  
  78. device has been removed until a subsequent SCSI command fails which will  
  79. probably cause that device to be set offline by the mid level. An LLD that  
  80. detects the removal of a SCSI device can instigate its removal from  
  81. upper layers with this sequence:  
  82.                   SCSI DEVICE hot unplug  
  83. LLD                      mid level                 LLD  
  84. ===----------------------=========-----------------===------  
  85. scsi_remove_device() -------+  
  86.                             |  
  87.                      slave_destroy()  
  88. ------------------------------------------------------------  
  89. It may be useful for an LLD to keep track of struct scsi_device instances  
  90. (a pointer is passed as the parameter to slave_alloc() and  
  91. slave_configure() callbacks). Such instances are "owned" by the mid-level.  
  92. struct scsi_device instances are freed after slave_destroy().