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\n");
		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\n");
		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\n");
		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\n",
				(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
};

  

时间: 2024-10-08 08:50:41

Linux设备驱动框架设计的相关文章

Linux设备驱动核心理论(三)

10.中断与时钟 10.1 中断与定时器 所谓中断是指CPU在执行程序的过程中,出现了某些突发事件急待处理,CPU必须暂停执行当前程序,转去处理突发事件,处理完毕后CPU又返回原程序被中断的位置并继续执行. 根据中断的来源,中断可分为内部中断和外部中断,内部中断的中断来源来自CPU内部(软件中断.溢出.除法错误等,例如,操作系统从用户态切换到内核态需借助CPU内部的软件中断),外部中断的中断来源来自CPU外部,由外设提出请求. 根据中断是否可以屏蔽分为可屏蔽中断与不屏蔽中断(NMI),可屏蔽中断

[连载]《C#通讯(串口和网络)框架的设计与实现》-3.设备驱动的设计

目       录 第三章           设备驱动的设计... 2 3.1           初始化设备... 4 3.2           运行设备接口设计... 4 3.3           虚拟设备接口设计... 6 3.4           协议驱动设计... 7 3.5           命令缓存设计... 17 3.6           数据持久化设计... 24 3.7           IO数据交互设计... 26 3.8           通讯状态设计..

linux设备驱动之misc驱动框架源码分析(一)

1.misc设备驱动框架源码部分是由内核开发者实现提供的,主要是创建misc类和为驱动开发者提供misc_register函数,来进行创建misc设备. 这部分的源码在/drvier/char/misc.c里,代码如下: /*  * linux/drivers/char/misc.c  *  * Generic misc open routine by Johan Myreen  *  * Based on code from Linus  *  * Teemu Rantanen's Micro

Linux设备驱动基本框架(字符设备)

Linux设备驱动都是以内核模块的形式出现的,但模块不一定是驱动.驱动可以编译进内核,在配置内核时,如果把某个配置项设为m,那么它将会随着内核编译时被编译成一个模块,但是这样向内核添加驱动程序会使得内核变得很大,而且在增加.修改.删除驱动程序时需要再一次编译内核,这样做极为麻烦.所以一般的驱动程序都是采用模块化装载,在需要使用时候通过insmod装载进内核,不需要使用时用rmmod卸载驱动模块. 内核模块的主要相关命令: lsmod:查看当前内核装载有哪些模块 insmod:加载模块 rmmod

【转】Linux设备驱动--块设备(一)之概念和框架

原文地址:Linux设备驱动--块设备(一)之概念和框架 基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到缓冲区. 字符设备(Character device) ---是一个顺序的数据流设备,对这种设备的读写是按字符进行的,而且这些字符是连续地形成一个数据流.他不具备缓冲区,所以对这种设备的读写是实时的. 扇区(Sectors):任何块设备硬

《Linux设备驱动开发详解(第3版)》海量更新总结

本博实时更新<Linux设备驱动开发详解(第3版)>的最新进展. 2015.2.26 几乎完成初稿. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux设备驱动概述及开发环境构建>[D]删除关于LDD6410开发板的介绍[F]更新新的Ubuntu虚拟机[N]添加关于QEMU模拟vexpress板的描述 第2章 <驱动设计的硬件基础> [N]增加关于SoC的介绍:[N]增加关于eFuse的内容:[D]删除ISA总线的内容了:[N]增加关于SP

Linux设备驱动开发基础

1.驱动概述和开发环境搭建 1.1驱动设备的作用 对设备驱动最通俗的解释就是"驱动硬件设备行动".驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮训.中断处理.DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据. 由此可见,设备驱动充当了硬件和应用软件之间的纽带,他使得应用软件只需要调用系统软件的应用编程接口(API)就可让硬件去完成要求的工作.在系统中没有操作系统的情况下,工

Linux设备驱动模型--分离分层思想

一.设备驱动的分层思想:以platform设备驱动.input设备驱动为例看看他们在分层思想上的体现 [1]在核心层:一类设备总线通用的功能特性,如果某一设备支持的总线比较特别可以不使用这些功能特性,在自己的设备总线结构体中 进行重载. 例(1)platform核心层:定义在drivers/base/platform.c文件 Linux内核对设备总线先进行了一次全局的抽象,即概括了所有设备驱动总线具有的功能特性: struct bus_type { //具备总线名.总线属性.总线上设备属性.总线

《Linux设备驱动开发具体解释(第3版)》进展同步更新

本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTEX-A9平台. [F]是修正或升级:[N]是新增知识点:[D]是删除的内容 第1章 <Linux设备驱动概述及开发环境构建>[D]删除关于LDD6410开发板的介绍[F]更新新的Ubuntu虚拟机[N]加入关于QEMU模拟vexpress板的描写叙述 第2章 <驱动设计的硬件基础> [