Linux设备驱动框架设计
引子
Linux操作系统的一大优势就是支持数以万计的芯片设备,大大小小的芯片厂商工程师都在积极地向Linux kernel提交设备驱动代码。能让这个目标得以实现,这背后隐藏着一个看不见的技术优势:Linux内核提供了一套易于扩展和维护的设备驱动框架。Linux内核本身提供一套设备驱动模型,此模型提供了Linux内核对设备的一般性抽象描述,包括设备的电源管理、对象生命周期管理、用户空间呈现等等。在设备模型的帮助下,设备驱动开发工程师从设备的一般性抽象中解脱出来。但是每个设备的具体功能实现还需要大量开发工作,如果每个设备都从头开发,那工作量无疑相当巨大。而这些设备可以按功能进行分类,每个设备类在业界或者标准组织中定义了硬件标准规范,所以针对每个设备类,如果有一个针对此设备类的功能性抽象框架,这将大大加快新设备的添加和开发效率。设备标准规范的存在,无疑对设备驱动框架的设计提供了有力的支撑。
但是坏消息也不少:
- 标准规范往往落后于产品,特别对新兴设备来说。
- 一个设备类可能是一套标准规范定义,也有可能是多套标准规范定义。
- 芯片厂商之间,为了达到提升自己的技术优势、市场壁垒等目的,在标准规范之内或之外,做了很多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字段继承大部分的功能。
框架设计相关代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | 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对象即可完成新的设备驱动的添加。
框架设计相关代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | 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块设备驱动的添加。
框架设计相关代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | /* 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 }; |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?