// SPDX-License-Identifier: GPL-2.0 #include <linux/blkdev.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <scsi/scsi_device.h> #include <scsi/scsi_devinfo.h> #include "scsi_priv.h" /* * scsi_dev_info_list: structure to hold black/white listed devices. */ struct scsi_dev_info_list { … }; struct scsi_dev_info_list_table { … }; static blist_flags_t scsi_default_dev_flags; static LIST_HEAD(scsi_dev_info_list); static char scsi_dev_flags[256]; /* * scsi_static_device_list: list of devices that require settings that differ * from the default, includes black-listed (broken) devices. The entries here * are added to the tail of scsi_dev_info_list via scsi_dev_info_list_init. * * If possible, set the BLIST_* flags from inside a SCSI LLD rather than * adding an entry to this list. */ static struct { … } scsi_static_device_list[] __initdata = …; static struct scsi_dev_info_list_table *scsi_devinfo_lookup_by_key(int key) { … } /* * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into * devinfo vendor and model strings. */ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length, char *from, int compatible) { … } /** * scsi_dev_info_list_add - add one dev_info list entry. * @compatible: if true, null terminate short strings. Otherwise space pad. * @vendor: vendor string * @model: model (product) string * @strflags: integer string * @flags: if strflags NULL, use this flag value * * Description: * Create and add one dev_info entry for @vendor, @model, @strflags or * @flag. If @compatible, add to the tail of the list, do not space * pad, and set devinfo->compatible. The scsi_static_device_list entries * are added with @compatible 1 and @clfags NULL. * * Returns: 0 OK, -error on failure. **/ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, char *strflags, blist_flags_t flags) { … } /** * scsi_dev_info_list_add_keyed - add one dev_info list entry. * @compatible: if true, null terminate short strings. Otherwise space pad. * @vendor: vendor string * @model: model (product) string * @strflags: integer string * @flags: if strflags NULL, use this flag value * @key: specify list to use * * Description: * Create and add one dev_info entry for @vendor, @model, * @strflags or @flag in list specified by @key. If @compatible, * add to the tail of the list, do not space pad, and set * devinfo->compatible. The scsi_static_device_list entries are * added with @compatible 1 and @clfags NULL. * * Returns: 0 OK, -error on failure. **/ int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, char *strflags, blist_flags_t flags, enum scsi_devinfo_key key) { … } EXPORT_SYMBOL(…); /** * scsi_dev_info_list_find - find a matching dev_info list entry. * @vendor: full vendor string * @model: full model (product) string * @key: specify list to use * * Description: * Finds the first dev_info entry matching @vendor, @model * in list specified by @key. * * Returns: pointer to matching entry, or ERR_PTR on failure. **/ static struct scsi_dev_info_list *scsi_dev_info_list_find(const char *vendor, const char *model, enum scsi_devinfo_key key) { … } /** * scsi_dev_info_list_del_keyed - remove one dev_info list entry. * @vendor: vendor string * @model: model (product) string * @key: specify list to use * * Description: * Remove and destroy one dev_info entry for @vendor, @model * in list specified by @key. * * Returns: 0 OK, -error on failure. **/ int scsi_dev_info_list_del_keyed(char *vendor, char *model, enum scsi_devinfo_key key) { … } EXPORT_SYMBOL(…); /** * scsi_dev_info_list_add_str - parse dev_list and add to the scsi_dev_info_list. * @dev_list: string of device flags to add * * Description: * Parse dev_list, and add entries to the scsi_dev_info_list. * dev_list is of the form "vendor:product:flag,vendor:product:flag". * dev_list is modified via strsep. Can be called for command line * addition, for proc or mabye a sysfs interface. * * Returns: 0 if OK, -error on failure. **/ static int scsi_dev_info_list_add_str(char *dev_list) { … } /** * scsi_get_device_flags - get device specific flags from the dynamic * device list. * @sdev: &scsi_device to get flags for * @vendor: vendor name * @model: model name * * Description: * Search the global scsi_dev_info_list (specified by list zero) * for an entry matching @vendor and @model, if found, return the * matching flags value, else return the host or global default * settings. Called during scan time. **/ blist_flags_t scsi_get_device_flags(struct scsi_device *sdev, const unsigned char *vendor, const unsigned char *model) { … } /** * scsi_get_device_flags_keyed - get device specific flags from the dynamic device list * @sdev: &scsi_device to get flags for * @vendor: vendor name * @model: model name * @key: list to look up * * Description: * Search the scsi_dev_info_list specified by @key for an entry * matching @vendor and @model, if found, return the matching * flags value, else return the host or global default settings. * Called during scan time. **/ blist_flags_t scsi_get_device_flags_keyed(struct scsi_device *sdev, const unsigned char *vendor, const unsigned char *model, enum scsi_devinfo_key key) { … } EXPORT_SYMBOL(…); #ifdef CONFIG_SCSI_PROC_FS struct double_list { … }; static int devinfo_seq_show(struct seq_file *m, void *v) { … } static void *devinfo_seq_start(struct seq_file *m, loff_t *ppos) { … } static void *devinfo_seq_next(struct seq_file *m, void *v, loff_t *ppos) { … } static void devinfo_seq_stop(struct seq_file *m, void *v) { … } static const struct seq_operations scsi_devinfo_seq_ops = …; static int proc_scsi_devinfo_open(struct inode *inode, struct file *file) { … } /* * proc_scsi_dev_info_write - allow additions to scsi_dev_info_list via /proc. * * Description: Adds a black/white list entry for vendor and model with an * integer value of flag to the scsi device info list. * To use, echo "vendor:model:flag" > /proc/scsi/device_info */ static ssize_t proc_scsi_devinfo_write(struct file *file, const char __user *buf, size_t length, loff_t *ppos) { … } static const struct proc_ops scsi_devinfo_proc_ops = …; #endif /* CONFIG_SCSI_PROC_FS */ module_param_string(…); MODULE_PARM_DESC(…) …; module_param_named(default_dev_flags, scsi_default_dev_flags, ullong, 0644); MODULE_PARM_DESC(…) …; /** * scsi_exit_devinfo - remove /proc/scsi/device_info & the scsi_dev_info_list **/ void scsi_exit_devinfo(void) { … } /** * scsi_dev_info_add_list - add a new devinfo list * @key: key of the list to add * @name: Name of the list to add (for /proc/scsi/device_info) * * Adds the requested list, returns zero on success, -EEXIST if the * key is already registered to a list, or other error on failure. */ int scsi_dev_info_add_list(enum scsi_devinfo_key key, const char *name) { … } EXPORT_SYMBOL(…); /** * scsi_dev_info_remove_list - destroy an added devinfo list * @key: key of the list to destroy * * Iterates over the entire list first, freeing all the values, then * frees the list itself. Returns 0 on success or -EINVAL if the key * can't be found. */ int scsi_dev_info_remove_list(enum scsi_devinfo_key key) { … } EXPORT_SYMBOL(…); /** * scsi_init_devinfo - set up the dynamic device list. * * Description: * Add command line entries from scsi_dev_flags, then add * scsi_static_device_list entries to the scsi device info list. */ int __init scsi_init_devinfo(void) { … }