引子

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 foundn");

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 entriesn");

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 devicen");

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;

}

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: %llun",

(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

};

嵌入式物联网资料分享交流群:707159742 入群有全套学习视频资料电子书免费赠送!

资料参考:

嵌入式底层 - linux设备驱动之I2C驱动框架 - 创客学院直播室​www.makeru.com.cn

linux pcie驱动框架_Linux设备驱动框架设计相关推荐

  1. linux下camera驱动分析_LINUX设备驱动模型分析之三 驱动模块相关(DRIVER)接口分析...

    本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> 上一章我们分析了bus-driver- ...

  2. linux i2c核心,总线与设备驱动,Linux2.6.37 I2C驱动框架分析(一)

    最近工作中又使用到了I2C,所以借S3C2440开发板GT2440为硬件平台温习一遍I2C驱动体系. linux内核中IIC驱动的体系框架 linux内核中IIC部分驱动代码位于:/drivers/i ...

  3. Linux SPI驱动框架(3)——设备驱动层

    SPI设备驱动层   Linux SPI驱动框架(1)和(2)中分别介绍了SPI框架中核心层,和控制器驱动层.其实实际开发过程中,不是IC原厂工程师比较少会接触控制器驱动层,设备驱动层才是接触比较多的 ...

  4. linux用户空间flash驱动,全面掌握Linux驱动框架——字符设备驱动、I2C驱动、总线设备驱动、NAND FLASH驱动...

    原标题:全面掌握Linux驱动框架--字符设备驱动.I2C驱动.总线设备驱动.NAND FLASH驱动 字符设备驱动 哈~ 这几天都在发图,通过这种方式,我们希望能帮大家梳理学过的知识,全局的掌握Li ...

  5. 一起分析Linux系统设计思想——05字符设备驱动框架剖析(四)

    在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习.我的使命就是过滤掉大量的垃圾信息,将知识体系化,以短平快的方式直 ...

  6. linux中流设备_Linux设备驱动子系统终极弹

    0. 预备理论 1. USB Core 2. USB Hub 3. USB OTG 4. USB Host 5. USB Gadget 6. USB Mass Storage USB博大精深,不是一两 ...

  7. 【linux驱动之字符设备驱动基础】

    linux驱动之字符设备驱动基础 文章目录 linux驱动之字符设备驱动基础 前言 一.开启驱动学习之路 二.驱动预备知识 三.什么是驱动? 3.1 驱动概念 3.2 linux 体系架构 3.3 模 ...

  8. Linux设备驱动篇——[I2C设备驱动-1]

    Linux 设备驱动篇之I2c设备驱动 fulinux 一.I2C驱动体系 虽然I2C硬件体系结构和协议都很容易理解,但是Linux I2C驱动体系结构却有相当的复杂度,它主要由3部分组成,即I2C设 ...

  9. Linux驱动之字符设备驱动

    系列文章目录 第一章 Linux入门之驱动框架 第二章 Linux驱动之字符设备驱动 文章目录 系列文章目录 前言 一.认识字符设备驱动 1.基本概念 2.基本概念 二.字符设备旧框架 1.注册和注销 ...

最新文章

  1. ERP已死,中台已凉,DDD称王!
  2. php 开发 比 java 快_PHP 比 Java 的开发效率高在哪?
  3. 糍粑大叔的独游之旅-开篇语
  4. Android DialogFragment 遇到 java.lang.IllegalStateException: Fragment already added: 的解决方法
  5. Springboot中的缓存Cache和CacheManager原理介绍
  6. JUnit学习摘要+入门实例 (junit4)
  7. 数组初始化 和 vector初始化
  8. Golang——文件创建和写入、OpenFile追加写入、Open读取文件、ReadBytes缓冲区读取、os.Args、flag
  9. BAT 面试中,遇到知识盲点如何巧妙圆场?
  10. 机器学习项目实战----信用卡欺诈检测(二)
  11. 软件测试-缺陷报告(自己看)
  12. 二叉搜索树的操作题集
  13. node.js 谷歌翻译api
  14. Modern C++ 书籍推荐
  15. python三国演义人物出场_Python教你查询《三国演义》书籍人物出场次序
  16. weak_ptr介绍
  17. Java:Hutool工具箱之Hutool-crypto加密解密
  18. 产品读书《人人都是产品经理 1.0》
  19. WIFI学习一(socket介绍)
  20. php 抓取网页内容与图片

热门文章

  1. C++ vector的用法
  2. Linux设备驱动之Ioctl控制
  3. 业务决定功能,功能决定技术
  4. what is the meaning of bring you up to speed?
  5. WayOs路由器WAN口帐号保存工具,可直接发送至邮箱,及保存接口VLAN号
  6. 遗传算法求解极大值问题
  7. 在IIS中配置MIME类型组建Wap网站
  8. Android网络框架OK3,Android网络框架---OkHttp3
  9. android camera分辨率设置,请问如何使用camera2设置全屏preview,要求适配所有屏幕尺寸?...
  10. 使用Java生成二维码图片(亲测)