• Linux设备驱动框架设计


    引子

    Linux操作系统的一大优势就是支持数以万计的芯片设备,大大小小的芯片厂商工程师都在积极地向Linux kernel提交设备驱动代码。能让这个目标得以实现,这背后隐藏着一个看不见的技术优势:Linux内核提供了一套易于扩展和维护的设备驱动框架。Linux内核本身提供一套设备驱动模型,此模型提供了Linux内核对设备的一般性抽象描述,包括设备的电源管理、对象生命周期管理、用户空间呈现等等。在设备模型的帮助下,设备驱动开发工程师从设备的一般性抽象中解脱出来。但是每个设备的具体功能实现还需要大量开发工作,如果每个设备都从头开发,那工作量无疑相当巨大。而这些设备可以按功能进行分类,每个设备类在业界或者标准组织中定义了硬件标准规范,所以针对每个设备类,如果有一个针对此设备类的功能性抽象框架,这将大大加快新设备的添加和开发效率。设备标准规范的存在,无疑对设备驱动框架的设计提供了有力的支撑。

    但是坏消息也不少:

    1. 标准规范往往落后于产品,特别对新兴设备来说。
    2. 一个设备类可能是一套标准规范定义,也有可能是多套标准规范定义。
    3. 芯片厂商之间,为了达到提升自己的技术优势、市场壁垒等目的,在标准规范之内或之外,做了很多vendor specific的实现。

    这些差异性需求下,一个设备类就像一颗树,其树干为设备标准规范(可能有多个,如下图),每个分支为厂商设备类或设备子类规范,而每片树叶就是每个具体的设备。设备框架的目的就是能帮助驱动工程师简洁优雅地添加一片树叶。这些差异性需求对框架设计是一个不小的挑战,如何很好地支持这些需求,是考验优秀设备驱动框架的试金石。

    本文的目的就是总结一些内核设备驱动框架的优秀设计方案,以供大家参考。如有疏漏,也欢迎大家留言补充。

    ATA设备驱动框架设计

    现状描述

    ATA驱动模块管理众多的SATA、PATA设备。以SATA设备为例,又分支持和不支持 port multiplier功能的,支持port multiplier的。而且SATA总线又存在多种标准规范定义的(ahci、fsl、 sil24 etc.),就算是用ahci 总线标准的,有些厂商总在某些地方做的跟标准不一致。

    设计要点

    • 所有功能点抽象成接口,再抽象成数据结构struct ata_port_operations,实现对象的多态;
    • 通过struct ata_port_operations的inherits字段实现对象的继承;
    • 新的设备驱动添加,就是添加新的struct ata_port_operations对象,而此对象可以从已有的相似对象节点上,通过inherits字段继承大部分的功能。

    框架设计相关代码示例:

    struct ata_port_operations {
    	/*
    	 * Command execution
    	 */
    	int  (*qc_defer)(struct ata_queued_cmd *qc);
    	int  (*check_atapi_dma)(struct ata_queued_cmd *qc);
    	void (*qc_prep)(struct ata_queued_cmd *qc);
    	unsigned int (*qc_issue)(struct ata_queued_cmd *qc);
    	bool (*qc_fill_rtf)(struct ata_queued_cmd *qc);
    
    	/*
    	 * Configuration and exception handling
    	 */
    	int  (*cable_detect)(struct ata_port *ap);
    	unsigned long (*mode_filter)(struct ata_device *dev, unsigned long xfer_mask);
    	void (*set_piomode)(struct ata_port *ap, struct ata_device *dev);
    	void (*set_dmamode)(struct ata_port *ap, struct ata_device *dev);
    	int  (*set_mode)(struct ata_link *link, struct ata_device **r_failed_dev);
    	unsigned int (*read_id)(struct ata_device *dev, struct ata_taskfile *tf, u16 *id);
    
    	void (*dev_config)(struct ata_device *dev);
    
    	void (*freeze)(struct ata_port *ap);
    	void (*thaw)(struct ata_port *ap);
    	ata_prereset_fn_t	prereset;
    	ata_reset_fn_t		softreset;
    	ata_reset_fn_t		hardreset;
    	ata_postreset_fn_t	postreset;
    	ata_prereset_fn_t	pmp_prereset;
    	ata_reset_fn_t		pmp_softreset;
    	ata_reset_fn_t		pmp_hardreset;
    	ata_postreset_fn_t	pmp_postreset;
    	void (*error_handler)(struct ata_port *ap);
    	void (*lost_interrupt)(struct ata_port *ap);
    	void (*post_internal_cmd)(struct ata_queued_cmd *qc);
    
    	/*
    	 * Optional features
    	 */
    	int  (*scr_read)(struct ata_link *link, unsigned int sc_reg, u32 *val);
    	int  (*scr_write)(struct ata_link *link, unsigned int sc_reg, u32 val);
    	void (*pmp_attach)(struct ata_port *ap);
    	void (*pmp_detach)(struct ata_port *ap);
    	int  (*enable_pm)(struct ata_port *ap, enum link_pm policy);
    	void (*disable_pm)(struct ata_port *ap);
    
    	/*
    	 * Start, stop, suspend and resume
    	 */
    	int  (*port_suspend)(struct ata_port *ap, pm_message_t mesg);
    	int  (*port_resume)(struct ata_port *ap);
    	int  (*port_start)(struct ata_port *ap);
    	void (*port_stop)(struct ata_port *ap);
    	void (*host_stop)(struct ata_host *host);
    
    #ifdef CONFIG_ATA_SFF
    	/*
    	 * SFF / taskfile oriented ops
    	 */
    	void (*sff_dev_select)(struct ata_port *ap, unsigned int device);
    	u8   (*sff_check_status)(struct ata_port *ap);
    	u8   (*sff_check_altstatus)(struct ata_port *ap);
    	void (*sff_tf_load)(struct ata_port *ap, const struct ata_taskfile *tf);
    	void (*sff_tf_read)(struct ata_port *ap, struct ata_taskfile *tf);
    	void (*sff_exec_command)(struct ata_port *ap,
    				 const struct ata_taskfile *tf);
    	unsigned int (*sff_data_xfer)(struct ata_device *dev,
    			unsigned char *buf, unsigned int buflen, int rw);
    	u8   (*sff_irq_on)(struct ata_port *);
    	void (*sff_irq_clear)(struct ata_port *);
    
    	void (*bmdma_setup)(struct ata_queued_cmd *qc);
    	void (*bmdma_start)(struct ata_queued_cmd *qc);
    	void (*bmdma_stop)(struct ata_queued_cmd *qc);
    	u8   (*bmdma_status)(struct ata_port *ap);
    
    	void (*drain_fifo)(struct ata_queued_cmd *qc);
    #endif /* CONFIG_ATA_SFF */
    
    	ssize_t (*em_show)(struct ata_port *ap, char *buf);
    	ssize_t (*em_store)(struct ata_port *ap, const char *message,
    			    size_t size);
    	ssize_t (*sw_activity_show)(struct ata_device *dev, char *buf);
    	ssize_t (*sw_activity_store)(struct ata_device *dev,
    				     enum sw_activity val);
    	/*
    	 * Obsolete
    	 */
    	void (*phy_reset)(struct ata_port *ap);
    	void (*eng_timeout)(struct ata_port *ap);
    
    	/*
    	 * ->inherits must be the last field and all the preceding
    	 * fields must be pointers.
    	 */
    	const struct ata_port_operations	*inherits;
    [在对象ata_port_operations 最后一个字段定义一个指向ata_port_operations 的指针。
    ata_port_operations 类似于 C++ 中的 vtable, 这里模仿 C++ 中继承基类vtable的子类vtable内存布局。]};
    
    const struct ata_port_operations ata_base_port_ops = {
    	.prereset		= ata_std_prereset,
    	.postreset		= ata_std_postreset,
    	.error_handler		= ata_std_error_handler,
    };
    [基类vtable]
    const struct ata_port_operations sata_port_ops = {
    	.inherits		= &ata_base_port_ops,
    
    	.qc_defer		= ata_std_qc_defer,
    	.hardreset		= sata_std_hardreset,
    };[继承ata_base_port_ops的子类vtable]
    
    const struct ata_port_operations sata_pmp_port_ops = {
    	.inherits		= &sata_port_ops,
    	.pmp_prereset		= ata_std_prereset,
    	.pmp_hardreset		= sata_std_hardreset,
    	.pmp_postreset		= ata_std_postreset,
    	.error_handler		= sata_pmp_error_handler,
    };
    
    
    static struct ata_port_operations ahci_ops = {
    	.inherits		= &sata_pmp_port_ops,
    
    	.qc_defer		= sata_pmp_qc_defer_cmd_switch,
    	.qc_prep		= ahci_qc_prep,
    	.qc_issue		= ahci_qc_issue,
    	.qc_fill_rtf		= ahci_qc_fill_rtf,
    
    	.freeze			= ahci_freeze,
    	.thaw			= ahci_thaw,
    	.softreset		= ahci_softreset,
    	.hardreset		= ahci_hardreset,
    	.postreset		= ahci_postreset,
    	.pmp_softreset		= ahci_softreset,
    	.error_handler		= ahci_error_handler,
    	.post_internal_cmd	= ahci_post_internal_cmd,
    	.dev_config		= ahci_dev_config,
    
    	.scr_read		= ahci_scr_read,
    	.scr_write		= ahci_scr_write,
    	.pmp_attach		= ahci_pmp_attach,
    	.pmp_detach		= ahci_pmp_detach,
    
    	.enable_pm		= ahci_enable_alpm,
    	.disable_pm		= ahci_disable_alpm,
    	.em_show		= ahci_led_show,
    	.em_store		= ahci_led_store,
    	.sw_activity_show	= ahci_activity_show,
    	.sw_activity_store	= ahci_activity_store,
    #ifdef CONFIG_PM
    	.port_suspend		= ahci_port_suspend,
    	.port_resume		= ahci_port_resume,
    #endif
    	.port_start		= ahci_port_start,
    	.port_stop		= ahci_port_stop,
    };
    
    static struct ata_port_operations ahci_vt8251_ops = {
    	.inherits		= &ahci_ops,
    	.hardreset		= ahci_vt8251_hardreset,
    };[继承 ahci_ops的子类 vtable]
    
    
    static const struct ata_port_info ahci_port_info[] = {
    	[board_ahci] =
    	{
    		.flags		= AHCI_FLAG_COMMON,
    		.pio_mask	= ATA_PIO4,
    		.udma_mask	= ATA_UDMA6,
    		.port_ops	= &ahci_ops,
    	},
    	[board_ahci_vt8251] =
    	{
    		AHCI_HFLAGS	(AHCI_HFLAG_NO_NCQ | AHCI_HFLAG_NO_PMP),
    		.flags		= AHCI_FLAG_COMMON,
    		.pio_mask	= ATA_PIO4,
    		.udma_mask	= ATA_UDMA6,
    		.port_ops	= &ahci_vt8251_ops,[初始化 对象ahci_port_info[board_ahci_vt8251] 的vtable入口port_ops 为ahci_vt8251_ops]
    	},
    }
    
    static void ata_finalize_port_ops(struct ata_port_operations *ops)
    {
    	static DEFINE_SPINLOCK(lock);
    	const struct ata_port_operations *cur;
    	void **begin = (void **)ops;
    	void **end = (void **)&ops->inherits;
    	void **pp;
    
    	if (!ops || !ops->inherits)
    		return;
    
    	spin_lock(&lock);
    
    	for (cur = ops->inherits; cur; cur = cur->inherits) {
    		void **inherit = (void **)cur;
    
    		for (pp = begin; pp < end; pp++, inherit++)
    			if (!*pp)
    				*pp = *inherit;
    	}
    
    	for (pp = begin; pp < end; pp++)
    		if (IS_ERR(*pp))
    			*pp = NULL;
    
    	ops->inherits = NULL;
    [扫描多重继承的虚函数接口]
    	spin_unlock(&lock);
    }
    
    int ata_host_start(struct ata_host *host)
    {
    	int have_stop = 0;
    	void *start_dr = NULL;
    	int i, rc;
    
    	if (host->flags & ATA_HOST_STARTED)
    		return 0;
    
    	ata_finalize_port_ops(host->ops);[host对象初始化时,调用ata_finalize_port_ops初始化对象vtable指针host->ops]
     …
    
    }
    

    单板控制领域模型设计与实现》采用此驱动框架设计开发。

    PMBus设备驱动框架设计

    现状描述

    PMBus有一套标准规范,其中有些是基本功能,有些是可选功能。基本功能是必须要实现的,而且寄存器接口也进行标准化。而可选功能由各设备厂商自由决定,而且这些可选功能的寄存器接口也无统一规范,支持PMBus设备厂商的自定义寄存器。

    设计要点

    • 通过struct pmbus_data对pmbus设备进行抽象性描述,此对象聚合对象pmbus_driver_info和pmbus_sensor。通过struct pmbus_driver_info对pmbus设备标准规范进行抽象性描述,struct pmbus_sensor对pmbus传感器进行抽象性描述。
    • 可选功能集通过pmbus_driver_info的u32 func[PMBUS_PAGES]字段描述。
    • 非标准寄存器通过虚拟寄存器(Virtual registers)统一到pmbus驱动框架中。虚拟寄存器到设备自定义寄存器的映射通过pmbus_driver_info的4个接口:read_byte_data/read_word_data/write_word_data/write_byte来完成。 
    • 所以当添加一个新设备时,其差异性需求都封装在pmbus_driver_info中,这样pmbus_data和pmbus_sensor做为公共功能则无需修改,
    • 通过构造新的pmbus_driver_info对象即可完成新的设备驱动的添加。

    框架设计相关代码示例:

    struct pmbus_data {
    	struct device *dev;
    	struct device *hwmon_dev;
    
    	u32 flags;		/* from platform data */
    
    	int exponent;		/* linear mode: exponent for output voltages */
    
    	const struct pmbus_driver_info *info;
    
    	int max_attributes;
    	int num_attributes;
    	struct attribute_group group;
    
    	struct pmbus_sensor *sensors;
    
    	struct mutex update_lock;
    	bool valid;
    	unsigned long last_updated;	/* in jiffies */
    
    	/*
    	 * A single status register covers multiple attributes,
    	 * so we keep them all together.
    	 */
    	u8 status[PB_NUM_STATUS_REG];
    	u8 status_register;
    
    	u8 currpage;
    };
    struct pmbus_driver_info {
    	int pages;		/* Total number of pages */
    	enum pmbus_data_format format[PSC_NUM_CLASSES];
    	/*
    	 * Support one set of coefficients for each sensor type
    	 * Used for chips providing data in direct mode.
    	 */
    	int m[PSC_NUM_CLASSES];	/* mantissa for direct data format */
    	int b[PSC_NUM_CLASSES];	/* offset */
    	int R[PSC_NUM_CLASSES];	/* exponent */
    
    	u32 func[PMBUS_PAGES];	/* Functionality, per page */
    	/*
    	 * The following functions map manufacturing specific register values
    	 * to PMBus standard register values. Specify only if mapping is
    	 * necessary.
    	 * Functions return the register value (read) or zero (write) if
    	 * successful. A return value of -ENODATA indicates that there is no
    	 * manufacturer specific register, but that a standard PMBus register
    	 * may exist. Any other negative return value indicates that the
    	 * register does not exist, and that no attempt should be made to read
    	 * the standard register.
    	 */
    	int (*read_byte_data)(struct i2c_client *client, int page, int reg);
    	int (*read_word_data)(struct i2c_client *client, int page, int reg);
    	int (*write_word_data)(struct i2c_client *client, int page, int reg,
    			       u16 word);
    	int (*write_byte)(struct i2c_client *client, int page, u8 value);
    	/*
    	 * The identify function determines supported PMBus functionality.
    	 * This function is only necessary if a chip driver supports multiple
    	 * chips, and the chip functionality is not pre-determined.
    	 */
    	int (*identify)(struct i2c_client *client,
    			struct pmbus_driver_info *info);
    };
    struct pmbus_sensor {
    	struct pmbus_sensor *next;
    	char name[PMBUS_NAME_SIZE];	/* sysfs sensor name */
    	struct device_attribute attribute;
    	u8 page;		/* page number */
    	u16 reg;		/* register */
    	enum pmbus_sensor_classes class;	/* sensor class */
    	bool update;		/* runtime sensor update needed */
    	int data;		/* Sensor data.
    				   Negative if there was a read error */
    };
    
    static struct pmbus_driver_info tps53667_info = {
    	.pages = 1,
    	.format[PSC_VOLTAGE_IN] = linear,
    	.format[PSC_VOLTAGE_OUT] = vid,
    	.format[PSC_TEMPERATURE] = linear,
    	.format[PSC_CURRENT_IN] = linear,
    	.format[PSC_CURRENT_OUT] = linear,
    	.format[PSC_POWER] = linear,
    	.read_word_data = tps53667_read_word_data,
    	.write_word_data = tps53667_write_word_data,
    	.func[0] = PMBUS_HAVE_VIN	    |
                PMBUS_HAVE_VOUT		    |
                PMBUS_HAVE_IIN		    |
                PMBUS_HAVE_IOUT		    |
                PMBUS_HAVE_PIN		    |
                PMBUS_HAVE_POUT		    |
                PMBUS_HAVE_TEMP		    |
                PMBUS_HAVE_STATUS_VOUT	|
                PMBUS_HAVE_STATUS_IOUT	|
                PMBUS_HAVE_STATUS_INPUT	|
                PMBUS_HAVE_STATUS_TEMP,
    
    };
    
    static int tps53667_probe(struct i2c_client *client,
    			  const struct i2c_device_id *id)
    {
    	return pmbus_do_probe(client, id, &tps53667_info);
    }
    
    int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
    		   struct pmbus_driver_info *info)
    {
    	struct device *dev = &client->dev;
    	const struct pmbus_platform_data *pdata = dev->platform_data;
    	struct pmbus_data *data;
    	int ret;
    
    	if (!info)
    		return -ENODEV;
    
    	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE
    				     | I2C_FUNC_SMBUS_BYTE_DATA
    				     | I2C_FUNC_SMBUS_WORD_DATA))
    		return -ENODEV;
    
    	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    	if (!data)
    		return -ENOMEM;
    
    	i2c_set_clientdata(client, data);
    	mutex_init(&data->update_lock);
    	data->dev = dev;
    
    	if (pdata)
    		data->flags = pdata->flags;
    	data->info = info;
    
    	ret = pmbus_init_common(client, data, info);
    	if (ret < 0)
    		return ret;
    
    	ret = pmbus_find_attributes(client, data);
    	if (ret)
    		goto out_kfree;
    
    	/*
    	 * If there are no attributes, something is wrong.
    	 * Bail out instead of trying to register nothing.
    	 */
    	if (!data->num_attributes) {
    		dev_err(dev, "No attributes found
    ");
    		ret = -ENODEV;
    		goto out_kfree;
    	}
    
    	/* Register sysfs hooks */
    	ret = sysfs_create_group(&dev->kobj, &data->group);
    	if (ret) {
    		dev_err(dev, "Failed to create sysfs entries
    ");
    		goto out_kfree;
    	}
    	data->hwmon_dev = hwmon_device_register(dev);
    	if (IS_ERR(data->hwmon_dev)) {
    		ret = PTR_ERR(data->hwmon_dev);
    		dev_err(dev, "Failed to register hwmon device
    ");
    		goto out_hwmon_device_register;
    	}
        
        ret = device_create_file(dev, &dev_attr_clear_fault);
        if (ret)
            goto out_hwmon_device_register;    
        
    	return 0;
    
    out_hwmon_device_register:
    	sysfs_remove_group(&dev->kobj, &data->group);
    out_kfree:
    	kfree(data->group.attrs);
    	return ret;
    }
    

    linux PMBus总线及设备驱动分析》是对PMBus设备的进一步介绍。  

     USB块设备驱动框架设计

    现状描述

    USB块设备有一套标准规范定义,但USB设备类型众多(如U盘、MP3播放器、手机、GPS设备等等),各厂商水平参差不齐,实现混乱。而且因为USB规范相对较新,一直在演进变化中,这也加剧了这一混乱。比如:

    • 设备描述符中subclass、protocol字段为空或者不正确;
    • 应答sense长度非标准;
    • INQUIRY设备请求非标准;
    • bulk传输协议中的 tag 不匹配;
    • 类似问题近20个

    另外,Linux内核已有一套通用的SCSI块设备驱动框架,如果USB块设备能复用这个框架,那是一个最好的选择。

    设计要点

    • 通过struct scsi_host_template对scsi块设备进行抽象性描述,USB块设备通过创建一个scsi_host_template对象,集成到SCSI块设备驱动框架中;
    • 通过struct us_data对USB块设备进行抽象性描述,通过us_data的fflags字段和unusual_dev字段对USB块设备中的非标准行为进行抽象性描述;
    • 通过struct usb_device_id对USB块设备vendor-specific属性进行抽象描述,这样通过添加一个新的usb_device_id实例,即可完成对一个新的USB块设备驱动的添加。

    框架设计相关代码示例:

    /* Driver for USB Mass Storage compliant devices
     * SCSI layer glue code
    
    #define US_DO_ALL_FLAGS						
    	US_FLAG(SINGLE_LUN,	0x00000001)			
    		/* allow access to only LUN 0 */		
    	US_FLAG(NEED_OVERRIDE,	0x00000002)			
    		/* unusual_devs entry is necessary */		
    	US_FLAG(SCM_MULT_TARG,	0x00000004)			
    		/* supports multiple targets */			
    	US_FLAG(FIX_INQUIRY,	0x00000008)			
    		/* INQUIRY response needs faking */		
    	US_FLAG(FIX_CAPACITY,	0x00000010)			
    		/* READ CAPACITY response too big */		
    	US_FLAG(IGNORE_RESIDUE,	0x00000020)			
    		/* reported residue is wrong */			
    	US_FLAG(BULK32,		0x00000040)			
    		/* Uses 32-byte CBW length */			
    	US_FLAG(NOT_LOCKABLE,	0x00000080)			
    		/* PREVENT/ALLOW not supported */		
    	US_FLAG(GO_SLOW,	0x00000100)			
    		/* Need delay after Command phase */		
    	US_FLAG(NO_WP_DETECT,	0x00000200)			
    		/* Don't check for write-protect */		
    	US_FLAG(MAX_SECTORS_64,	0x00000400)			
    		/* Sets max_sectors to 64    */			
    	US_FLAG(IGNORE_DEVICE,	0x00000800)			
    		/* Don't claim device */			
    	US_FLAG(CAPACITY_HEURISTICS,	0x00001000)		
    		/* sometimes sizes is too big */		
    	US_FLAG(MAX_SECTORS_MIN,0x00002000)			
    		/* Sets max_sectors to arch min */		
    	US_FLAG(BULK_IGNORE_TAG,0x00004000)			
    		/* Ignore tag mismatch in bulk operations */    
    	US_FLAG(SANE_SENSE,     0x00008000)			
    		/* Sane Sense (> 18 bytes) */			
    	US_FLAG(CAPACITY_OK,	0x00010000)			
    		/* READ CAPACITY response is correct */		
    	US_FLAG(BAD_SENSE,	0x00020000)			
    		/* Bad Sense (never more than 18 bytes) */	
    	US_FLAG(NO_READ_DISC_INFO,	0x00040000)		
    		/* cannot handle READ_DISC_INFO */		
    	US_FLAG(NO_READ_CAPACITY_16,	0x00080000)		
    		/* cannot handle READ_CAPACITY_16 */		
    	US_FLAG(INITIAL_READ10,	0x00100000)			
    		/* Initial READ(10) (and others) must be retried */	
    	US_FLAG(WRITE_CACHE,	0x00200000)			
    		/* Write Cache status is not available */
    
    #define US_FLAG(name, value)	US_FL_##name = value ,
    enum { US_DO_ALL_FLAGS };
    #undef US_FLAG
    
    struct us_data {
    	/* The device we're working with
    	 * It's important to note:
    	 *    (o) you must hold dev_mutex to change pusb_dev
    	 */
    	struct mutex		dev_mutex;	 /* protect pusb_dev */
    	struct usb_device	*pusb_dev;	 /* this usb_device */
    	struct usb_interface	*pusb_intf;	 /* this interface */
    	struct us_unusual_dev   *unusual_dev;	 /* device-filter entry     */
    	unsigned long		fflags;		 /* fixed flags from filter */
    	unsigned long		dflags;		 /* dynamic atomic bitflags */
    	unsigned int		send_bulk_pipe;	 /* cached pipe values */
    	unsigned int		recv_bulk_pipe;
    	unsigned int		send_ctrl_pipe;
    	unsigned int		recv_ctrl_pipe;
    	unsigned int		recv_intr_pipe;
    
    	/* information about the device */
    	char			*transport_name;
    	char			*protocol_name;
    	__le32			bcs_signature;
    	u8			subclass;
    	u8			protocol;
    	u8			max_lun;
    
    	u8			ifnum;		 /* interface number   */
    	u8			ep_bInterval;	 /* interrupt interval */ 
    
    	/* function pointers for this device */
    	trans_cmnd		transport;	 /* transport function	   */
    	trans_reset		transport_reset; /* transport device reset */
    	proto_cmnd		proto_handler;	 /* protocol handler	   */
    
    	/* SCSI interfaces */
    	struct scsi_cmnd	*srb;		 /* current srb		*/
    	unsigned int		tag;		 /* current dCBWTag	*/
    	char			scsi_name[32];	 /* scsi_host name	*/
    
    	/* control and bulk communications data */
    	struct urb		*current_urb;	 /* USB requests	 */
    	struct usb_ctrlrequest	*cr;		 /* control requests	 */
    	struct usb_sg_request	current_sg;	 /* scatter-gather req.  */
    	unsigned char		*iobuf;		 /* I/O buffer		 */
    	dma_addr_t		iobuf_dma;	 /* buffer DMA addresses */
    	struct task_struct	*ctl_thread;	 /* the control thread   */
    
    	/* mutual exclusion and synchronization structures */
    	struct completion	cmnd_ready;	 /* to sleep thread on	    */
    	struct completion	notify;		 /* thread begin/end	    */
    	wait_queue_head_t	delay_wait;	 /* wait during reset	    */
    	struct delayed_work	scan_dwork;	 /* for async scanning      */
    
    	/* subdriver information */
    	void			*extra;		 /* Any extra data          */
    	extra_data_destructor	extra_destructor;/* extra data destructor   */
    #ifdef CONFIG_PM
    	pm_hook			suspend_resume_hook;
    #endif
    
    	/* hacks for READ CAPACITY bug handling */
    	int			use_last_sector_hacks;
    	int			last_sector_retries;
    };
    
    struct usb_device_id {
    	/* which fields to match against? */
    	__u16		match_flags;
    
    	/* Used for product specific matches; range is inclusive */
    	__u16		idVendor;
    	__u16		idProduct;
    	__u16		bcdDevice_lo;
    	__u16		bcdDevice_hi;
    
    	/* Used for device class matches */
    	__u8		bDeviceClass;
    	__u8		bDeviceSubClass;
    	__u8		bDeviceProtocol;
    
    	/* Used for interface class matches */
    	__u8		bInterfaceClass;
    	__u8		bInterfaceSubClass;
    	__u8		bInterfaceProtocol;
    
    	/* Used for vendor-specific interface matches */
    	__u8		bInterfaceNumber;
    
    	/* not matched against */
    	kernel_ulong_t	driver_info
    		__attribute__((aligned(sizeof(kernel_ulong_t))));
    };
    
    /*
     * The table of devices
     */
    #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, 
    		    vendorName, productName, useProtocol, useTransport, 
    		    initFunction, flags) 
    { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), 
      .driver_info = (flags) }
    
    struct usb_device_id usb_storage_usb_ids[] = {
    #	include "unusual_devs.h"
    	{ }		/* Terminating entry */
    };
      
    UNUSUAL_DEV(  0x22b8, 0x6426, 0x0101, 0x0101,
    		"Motorola",
    		"MSnc.",
    		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
    		US_FL_FIX_INQUIRY | US_FL_FIX_CAPACITY | US_FL_BULK_IGNORE_TAG),
    
    static int slave_configure(struct scsi_device *sdev)
    {
    ...
        if (us->fflags & US_FL_FIX_CAPACITY)
            sdev->fix_capacity = 1;    
    ...
    }        
    
    
    /*
     * read disk capacity
     */
    static void
    sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)
    {
    ...
    	/* Some devices are known to return the total number of blocks,
    	 * not the highest block number.  Some devices have versions
    	 * which do this and others which do not.  Some devices we might
    	 * suspect of doing this but we don't know for certain.
    	 *
    	 * If we know the reported capacity is wrong, decrement it.  If
    	 * we can only guess, then assume the number of blocks is even
    	 * (usually true but not always) and err on the side of lowering
    	 * the capacity.
    	 */
    	if (sdp->fix_capacity ||
    	    (sdp->guess_capacity && (sdkp->capacity & 0x01))) {
    		sd_printk(KERN_INFO, sdkp, "Adjusting the sector count "
    				"from its reported value: %llu
    ",
    				(unsigned long long) sdkp->capacity);
    		--sdkp->capacity;
    	}         
    ...
    }
    
    
    struct scsi_host_template usb_stor_host_template = {
    	/* basic userland interface stuff */
    	.name =				"usb-storage",
    	.proc_name =			"usb-storage",
    	.show_info =			show_info,
    	.write_info =			write_info,
    	.info =				host_info,
    
    	/* command interface -- queued only */
    	.queuecommand =			queuecommand,
    
    	/* error and abort handlers */
    	.eh_abort_handler =		command_abort,
    	.eh_device_reset_handler =	device_reset,
    	.eh_bus_reset_handler =		bus_reset,
    
    	/* queue commands only, only one command per LUN */
    	.can_queue =			1,
    	.cmd_per_lun =			1,
    
    	/* unknown initiator id */
    	.this_id =			-1,
    
    	.slave_alloc =			slave_alloc,
    	.slave_configure =		slave_configure,
    	.target_alloc =			target_alloc,
    
    	/* lots of sg segments can be handled */
    	.sg_tablesize =			SCSI_MAX_SG_CHAIN_SEGMENTS,
    
    	/* limit the total size of a transfer to 120 KB */
    	.max_sectors =                  240,
    
    	/* merge commands... this seems to help performance, but
    	 * periodically someone should test to see which setting is more
    	 * optimal.
    	 */
    	.use_clustering =		1,
    
    	/* emulated HBA */
    	.emulated =			1,
    
    	/* we do our own delay after a device or bus reset */
    	.skip_settle_delay =		1,
    
    	/* sysfs device attributes */
    	.sdev_attrs =			sysfs_device_attr_list,
    
    	/* module management */
    	.module =			THIS_MODULE
    };
    

      

  • 相关阅读:
    信息学奥赛一本通(c++版) 1003:对齐输出
    读书笔记(华科曹计昌 《c语言与程序设计》)
    使用request对象实现注册示例,get/post的编码问题
    Eclipse中开发第一个web(jsp)项目
    Eclipse恢复默认布局
    手工在tomcat目录中建立个人项目
    通过ServletContext获得工程根目录路径、读取文件以及获得classpath目录下的文件
    ServletContext设置全局变量实现统计站点访问次数
    servlet全局参数的设置
    Eclipse关联Servlet源码详细步骤
  • 原文地址:https://www.cnblogs.com/wahaha02/p/7273018.html
Copyright © 2020-2023  润新知