一.内核配置,配置使其支持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 - #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- unsigned char ch[8]={'a','b','c','d','e','f','g','h'};
- int main(int argc,char **argv)
- {
- int fd;
- unsigned char DEV[64];
- unsigned char PATH[64];
- unsigned char cmd[64];
- int i=0;
- int j=0;
-
- for(j=0;j<4;j++){
- for(i=0;i<8;i++){
- sprintf(PATH,"/media/sd%c%d/UpDate",ch[i],j);
- sprintf(DEV,"/media/sd%c%d",ch[i],j);
- fd=open(PATH,O_RDWR);
- if(fd==-1){
-
- continue;
- }
- else{
- printf("open device '%s'
",PATH);
- break;
- }
- }
- if(fd!=-1)
- break;
- }
- if(fd==-1){
- printf("can not find any u pan!
");
- }
- else{
- close(fd);
- sprintf(cmd,"sh %s %s",PATH,DEV);
- system(cmd);
- }
- return 0;
- }
这里cmd将设备文件路径作为第一个参数传递给脚本那么执行的脚本里面就可以通过$1获取DEV(/media/sda1)可以写个简单的脚本
测试下
[cpp] view plain copy - #! /bin/sh
- echo -e "update file!"
- echo $1
- ls -l $1
- touch /opt/1234
- 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 - usb 1-1: new high speed USB device using musb-hdrc and address 5
- usb 1-1: New USB device found, idVendor=0951, idProduct=1643
- usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
- usb 1-1: Product: DataTraveler G3
- usb 1-1: Manufacturer: Kingston
- usb 1-1: SerialNumber: 001CC0EC34F1FB90F71729FF
- scsi5 : usb-storage 1-1:1.0
- scsi 5:0:0:0: Direct-Access Kingston DataTraveler G3 1.00 PQ: 0 ANSI: 0 CCS
- sd 5:0:0:0: Attached scsi generic sg0 type 0
- sd 5:0:0:0: [sdb] 15644912 512-byte logical blocks: (8.01 GB/7.45 GiB)
- sd 5:0:0:0: [sdb] Write Protect is off
- sd 5:0:0:0: [sdb] Assuming drive cache: write through
- sd 5:0:0:0: [sdb] Assuming drive cache: write through
- sdb: sdb1
- sd 5:0:0:0: [sdb] Assuming drive cache: write through
- sd 5:0:0:0: [sdb] Attached SCSI removable disk
- FAT: invalid media value (0xb9)
- VFS: Can't find a valid FAT filesystem on dev sdb.
- EXT3-fs (sdb): error: can't find ext3 filesystem on dev sdb.
- EXT2-fs (sdb): error: can't find an ext2 filesystem on dev sdb.
- FAT: invalid media value (0xb9)
- VFS: Can't find a valid FAT filesystem on dev sdb.
- 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 - usb_stor_scan_thread
- scsi_scan_host
- do_scsi_scan_host
- scsi_scan_host_selected
- scsi_scan_channel
- __scsi_scan_target
- scsi_probe_and_add_lun
- scsi_add_lun
- 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 - SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
- char __user *, type, unsigned long, flags, void __user *, data)
- {
- int ret;
- char *kernel_type;
- char *kernel_dir;
- char *kernel_dev;
- unsigned long data_page;
-
- ret = copy_mount_string(type, &kernel_type);
- if (ret < 0)
- goto out_type;
-
- kernel_dir = getname(dir_name);
- if (IS_ERR(kernel_dir)) {
- ret = PTR_ERR(kernel_dir);
- goto out_dir;
- }
-
- ret = copy_mount_string(dev_name, &kernel_dev);
- if (ret < 0)
- goto out_dev;
-
- ret = copy_mount_options(data, &data_page);
- if (ret < 0)
- goto out_data;
-
- ret = do_mount(kernel_dev, kernel_dir, kernel_type, flags,
- (void *) data_page);
-
- call_usermodehelper ("/bin/update", NULL, NULL, 1);
- free_page(data_page);
- out_data:
- kfree(kernel_dev);
- out_dev:
- putname(kernel_dir);
- out_dir:
- kfree(kernel_type);
- out_type:
- return ret;
- }
虽然感觉硬件--应用层--设备驱动--应用程--内核--应用程--shell这样的路子别扭别扭的,但我觉得对我来说是最简单的招吧另一种方法:修改udev规则在/etc/udev/scripts下的mount.sh文件在"add"分支中添加/bin/update
四.简单的补充下u盘驱动的分析1.入口函数
[cpp] view plain copy - module_init(usb_stor_init);
- static int __init usb_stor_init(void)
- {
- int retval;
- printk("usb --- usb_stor_init start
");
- retval = usb_register(&usb_storage_driver);
- if (retval == 0)
- printk("ENE USB Mass Storage support registered.
");
- return retval;
- }
2.u盘设备驱动
[cpp] view plain copy - static struct usb_driver usb_storage_driver = {
- .name = "usb-storage",
- .probe = storage_probe,
- .disconnect = usb_stor_disconnect,
- .suspend = usb_stor_suspend,
- .resume = usb_stor_resume,
- .reset_resume = usb_stor_reset_resume,
- .pre_reset = usb_stor_pre_reset,
- .post_reset = usb_stor_post_reset,
- .id_table = usb_storage_usb_ids,
- .supports_autosuspend = 1,
- .soft_unbind = 1,
- };
3.支持设备id表
[cpp] view plain copy - struct usb_device_id usb_storage_usb_ids[] = {
- # include "unusual_devs.h" //包含头文件unusual_devs.h
- { }
- };
包含了一个unusual_devs.h头文件
该头文件包含了特殊的u盘设备id信息,也包含了通用的u盘设备类信息
[cpp] view plain copy -
- USUAL_DEV(USB_SC_RBC, USB_PR_CB, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_8020, USB_PR_CB, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_QIC, USB_PR_CB, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_UFI, USB_PR_CB, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_8070, USB_PR_CB, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_SCSI, USB_PR_CB, USB_US_TYPE_STOR),
-
- USUAL_DEV(USB_SC_RBC, USB_PR_CBI, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_8020, USB_PR_CBI, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_QIC, USB_PR_CBI, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_UFI, USB_PR_CBI, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_8070, USB_PR_CBI, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_SCSI, USB_PR_CBI, USB_US_TYPE_STOR),
-
- USUAL_DEV(USB_SC_RBC, USB_PR_BULK, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_8020, USB_PR_BULK, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_QIC, USB_PR_BULK, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_UFI, USB_PR_BULK, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_8070, USB_PR_BULK, USB_US_TYPE_STOR),
- USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0),
u盘驱动probe方法
1.probe方法storage_probe
[cpp] view plain copy - static int storage_probe(struct usb_interface *intf,const struct usb_device_id *id)
- {
- struct us_data *us;
- int result;
-
- if (usb_usual_check_type(id, USB_US_TYPE_STOR) || usb_usual_ignore_device(intf))
- return -ENXIO;
-
-
-
- result = usb_stor_probe1(&us, intf, id,(id - usb_storage_usb_ids) + us_unusual_dev_list);
- if (result)
- return result;
-
- result = usb_stor_probe2(us);
- return result;
- }
storage_probe分成了两部分,第一部分由usb_stor_probe1完成通用的配置,第二部分由usb_stor_probe2
2.第一部分probe usb_stor_probe1
[cpp] view plain copy - int usb_stor_probe1(struct us_data **pus,struct usb_interface *intf,const struct usb_device_id *id,struct us_unusual_dev *unusual_dev)
- {
- struct Scsi_Host *host;
- struct us_data *us;
- int result;
- US_DEBUGP("USB Mass Storage device detected
");
-
- host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));
- if (!host) {
- dev_warn(&intf->dev,"Unable to allocate the scsi host
");
- return -ENOMEM;
- }
- host->max_cmd_len = 16;
- host->sg_tablesize = usb_stor_sg_tablesize(intf);
- *pus = us = host_to_us(host);
- memset(us, 0, sizeof(struct us_data));
- mutex_init(&(us->dev_mutex));
- init_completion(&us->cmnd_ready);
- init_completion(&(us->notify));
- init_waitqueue_head(&us->delay_wait);
- init_completion(&us->scanning_done);
- result = associate_dev(us, intf);
- if (result)
- goto BadDevice;
- result = get_device_info(us, id, unusual_dev);
- if (result)
- goto BadDevice;
- get_transport(us);
- get_protocol(us);
- return 0;
- BadDevice:
- US_DEBUGP("storage_probe() failed
");
- release_everything(us);
- return result;
- }
- EXPORT_SYMBOL_GPL(usb_stor_probe1);
这里主要是调用了scsi_host_alloc分配了Scsi_host对象2.1 scsi_host_template对象
[cpp] view plain copy - struct scsi_host_template usb_stor_host_template = {
- .name = "usb-storage",
- .proc_name = "usb-storage",
- .proc_info = proc_info,
- .info = host_info,
- .queuecommand = queuecommand,
- .eh_abort_handler = command_abort,
- .eh_device_reset_handler = device_reset,
- .eh_bus_reset_handler = bus_reset,
- .can_queue = 1,
- .cmd_per_lun = 1,
- .this_id = -1,
- .slave_alloc = slave_alloc,
- .slave_configure = slave_configure,
- .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS,
- .max_sectors = 240,
- .use_clustering = 1,
- .emulated = 1,
- .skip_settle_delay = 1,
- .sdev_attrs = sysfs_device_attr_list,
- .module = THIS_MODULE
- };
在scsi接口底层会调用到它的多个方法3.第二部分probe usb_stor_probe2
[cpp] view plain copy - int usb_stor_probe2(struct us_data *us)
- {
- struct task_struct *th;
- int result;
- struct device *dev = &us->pusb_intf->dev;
- if (!us->transport || !us->proto_handler) {
- result = -ENXIO;
- goto BadDevice;
- }
- US_DEBUGP("Transport: %s
", us->transport_name);
- US_DEBUGP("Protocol: %s
", us->protocol_name);
- if (us->fflags & US_FL_SINGLE_LUN)
- us->max_lun = 0;
- result = get_pipes(us);
- if (result)
- goto BadDevice;
- result = usb_stor_acquire_resources(us);
- if (result)
- goto BadDevice;
- snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",dev_name(&us->pusb_intf->dev));
- result = scsi_add_host(us_to_host(us), dev);
- if (result) {
- dev_warn(dev,"Unable to add the scsi host
");
- goto BadDevice;
- }
- th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan");
- if (IS_ERR(th)) {
- dev_warn(dev,"Unable to start the device-scanning thread
");
- complete(&us->scanning_done);
- quiesce_and_remove_host(us);
- result = PTR_ERR(th);
- goto BadDevice;
- }
- usb_autopm_get_interface_no_resume(us->pusb_intf);
- wake_up_process(th);
- return 0;
- BadDevice:
- US_DEBUGP("storage_probe() failed
");
- release_everything(us);
- return result;
- }
- EXPORT_SYMBOL_GPL(usb_stor_probe2);
这里添加了Scsi_Host对象并创建了一个线程u盘扫描线程
[cpp] view plain copy - static int usb_stor_scan_thread(void * __us)
- {
- struct us_data *us = (struct us_data *)__us;
- struct device *dev = &us->pusb_intf->dev;
- dev_dbg(dev, "device found
");
- set_freezable();
- if (delay_use > 0) {
- dev_dbg(dev, "waiting for device to settle before scanning
");
- wait_event_freezable_timeout(us->delay_wait,test_bit(US_FLIDX_DONT_SCAN, &us->dflags),delay_use * HZ);
- }
- if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {
- if (us->protocol == USB_PR_BULK && !(us->fflags & US_FL_SINGLE_LUN)) {
- mutex_lock(&us->dev_mutex);
- us->max_lun = usb_stor_Bulk_max_lun(us);
- mutex_unlock(&us->dev_mutex);
- }
- scsi_scan_host(us_to_host(us));
- dev_dbg(dev, "scan complete
");
- }
- usb_autopm_put_interface(us->pusb_intf);
- complete_and_exit(&us->scanning_done, 0);
- }
在线程里会调用scsi_scan_host函数方法 这里面可以总结出u'盘的probe方法与下一层scsi接口的调用关系
[cpp] view plain copy - -- storage_probe
- scsi_host_alloc -- usb_stor_probe1
- scsi_add_host -- usb_stor_probe2
- scsi_scan_host -- usb_stor_scan_thread线程
- |
- slave_alloc()
- slave_configure()
在参考scsi的文档中有介绍这种模型
[cpp] view plain copy - Hotplug initialization model
- ============================
- In this model an LLD controls when SCSI hosts are introduced and removed
- from the SCSI subsystem. Hosts can be introduced as early as driver
- initialization and removed as late as driver shutdown. Typically a driver
- will respond to a sysfs probe() callback that indicates an HBA has been
- detected. After confirming that the new device is one that the LLD wants
- to control, the LLD will initialize the HBA and then register a new host
- with the SCSI mid level.
-
- During LLD initialization the driver should register itself with the
- appropriate IO bus on which it expects to find HBA(s) (e.g. the PCI bus).
- This can probably be done via sysfs. Any driver parameters (especially
- those that are writable after the driver is loaded) could also be
- registered with sysfs at this point. The SCSI mid level first becomes
- aware of an LLD when that LLD registers its first HBA.
-
- At some later time, the LLD becomes aware of an HBA and what follows
- is a typical sequence of calls between the LLD and the mid level.
- This example shows the mid level scanning the newly introduced HBA for 3
- scsi devices of which only the first 2 respond:
-
- HBA PROBE: assume 2 SCSI devices found in scan
- LLD mid level LLD
- ===-------------------=========--------------------===------
- scsi_host_alloc() -->
- scsi_add_host() ---->
- scsi_scan_host() -------+
- |
- slave_alloc()
- slave_configure() --> scsi_adjust_queue_depth()
- |
- slave_alloc()
- slave_configure()
- |
- slave_alloc() ***
- slave_destroy() ***
- ------------------------------------------------------------
-
- If the LLD wants to adjust the default queue settings, it can invoke
- scsi_adjust_queue_depth() in its slave_configure() routine.
-
- *** For scsi devices that the mid level tries to scan but do not
- respond, a slave_alloc(), slave_destroy() pair is called.
-
- When an HBA is being removed it could be as part of an orderly shutdown
- associated with the LLD module being unloaded (e.g. with the "rmmod"
- command) or in response to a "hot unplug" indicated by sysfs()'s
- remove() callback being invoked. In either case, the sequence is the
- same:
-
- HBA REMOVE: assume 2 SCSI devices attached
- LLD mid level LLD
- ===----------------------=========-----------------===------
- scsi_remove_host() ---------+
- |
- slave_destroy()
- slave_destroy()
- scsi_host_put()
- ------------------------------------------------------------
-
- It may be useful for a LLD to keep track of struct Scsi_Host instances
- (a pointer is returned by scsi_host_alloc()). Such instances are "owned"
- by the mid-level. struct Scsi_Host instances are freed from
- scsi_host_put() when the reference count hits zero.
-
- Hot unplugging an HBA that controls a disk which is processing SCSI
- commands on a mounted file system is an interesting situation. Reference
- counting logic is being introduced into the mid level to cope with many
- of the issues involved. See the section on reference counting below.
-
-
- The hotplug concept may be extended to SCSI devices. Currently, when an
- HBA is added, the scsi_scan_host() function causes a scan for SCSI devices
- attached to the HBA's SCSI transport. On newer SCSI transports the HBA
- may become aware of a new SCSI device _after_ the scan has completed.
- An LLD can use this sequence to make the mid level aware of a SCSI device:
-
- SCSI DEVICE hotplug
- LLD mid level LLD
- ===-------------------=========--------------------===------
- scsi_add_device() ------+
- |
- slave_alloc()
- slave_configure() [--> scsi_adjust_queue_depth()]
- ------------------------------------------------------------
-
- In a similar fashion, an LLD may become aware that a SCSI device has been
- removed (unplugged) or the connection to it has been interrupted. Some
- existing SCSI transports (e.g. SPI) may not become aware that a SCSI
- device has been removed until a subsequent SCSI command fails which will
- probably cause that device to be set offline by the mid level. An LLD that
- detects the removal of a SCSI device can instigate its removal from
- upper layers with this sequence:
-
- SCSI DEVICE hot unplug
- LLD mid level LLD
- ===----------------------=========-----------------===------
- scsi_remove_device() -------+
- |
- slave_destroy()
- ------------------------------------------------------------
-
- It may be useful for an LLD to keep track of struct scsi_device instances
- (a pointer is passed as the parameter to slave_alloc() and
- slave_configure() callbacks). Such instances are "owned" by the mid-level.
- struct scsi_device instances are freed after slave_destroy().