📄 ds.c
字号:
* pseudo multi-function card, we need to unbind * all devices */ did = p_dev->dev.driver_data; if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && (p_dev->socket->device_count != 0) && (p_dev->device_no == 0)) pcmcia_card_remove(p_dev->socket, p_dev); /* detach the "instance" */ if (!p_drv) return 0; if (p_drv->remove) p_drv->remove(p_dev); p_dev->dev_node = NULL; /* check for proper unloading */ if (p_dev->_irq || p_dev->_io || p_dev->_locked) printk(KERN_INFO "pcmcia: driver %s did not release config properly\n", p_drv->drv.name); for (i = 0; i < MAX_WIN; i++) if (p_dev->_win & CLIENT_WIN_REQ(i)) printk(KERN_INFO "pcmcia: driver %s did not release windows properly\n", p_drv->drv.name); /* references from pcmcia_probe_device */ pcmcia_put_dev(p_dev); module_put(p_drv->owner); return 0;}/* * pcmcia_device_query -- determine information about a pcmcia device */static int pcmcia_device_query(struct pcmcia_device *p_dev){ cistpl_manfid_t manf_id; cistpl_funcid_t func_id; cistpl_vers_1_t *vers1; unsigned int i; vers1 = kmalloc(sizeof(*vers1), GFP_KERNEL); if (!vers1) return -ENOMEM; if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_MANFID, &manf_id)) { p_dev->manf_id = manf_id.manf; p_dev->card_id = manf_id.card; p_dev->has_manf_id = 1; p_dev->has_card_id = 1; } if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_FUNCID, &func_id)) { p_dev->func_id = func_id.func; p_dev->has_func_id = 1; } else { /* rule of thumb: cards with no FUNCID, but with * common memory device geometry information, are * probably memory cards (from pcmcia-cs) */ cistpl_device_geo_t *devgeo; devgeo = kmalloc(sizeof(*devgeo), GFP_KERNEL); if (!devgeo) { kfree(vers1); return -ENOMEM; } if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_DEVICE_GEO, devgeo)) { ds_dbg(0, "mem device geometry probably means " "FUNCID_MEMORY\n"); p_dev->func_id = CISTPL_FUNCID_MEMORY; p_dev->has_func_id = 1; } kfree(devgeo); } if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_VERS_1, vers1)) { for (i=0; i < vers1->ns; i++) { char *tmp; unsigned int length; tmp = vers1->str + vers1->ofs[i]; length = strlen(tmp) + 1; if ((length < 2) || (length > 255)) continue; p_dev->prod_id[i] = kmalloc(sizeof(char) * length, GFP_KERNEL); if (!p_dev->prod_id[i]) continue; p_dev->prod_id[i] = strncpy(p_dev->prod_id[i], tmp, length); } } kfree(vers1); return 0;}/* device_add_lock is needed to avoid double registration by cardmgr and kernel. * Serializes pcmcia_device_add; will most likely be removed in future. * * While it has the caveat that adding new PCMCIA devices inside(!) device_register() * won't work, this doesn't matter much at the moment: the driver core doesn't * support it either. */static DEFINE_MUTEX(device_add_lock);struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function){ struct pcmcia_device *p_dev, *tmp_dev; unsigned long flags; int bus_id_len; s = pcmcia_get_socket(s); if (!s) return NULL; mutex_lock(&device_add_lock); ds_dbg(3, "adding device to %d, function %d\n", s->sock, function); /* max of 4 devices per card */ if (s->device_count == 4) goto err_put; p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL); if (!p_dev) goto err_put; p_dev->socket = s; p_dev->device_no = (s->device_count++); p_dev->func = function; p_dev->dev.bus = &pcmcia_bus_type; p_dev->dev.parent = s->dev.parent; p_dev->dev.release = pcmcia_release_dev; /* by default don't allow DMA */ p_dev->dma_mask = DMA_MASK_NONE; p_dev->dev.dma_mask = &p_dev->dma_mask; bus_id_len = sprintf (p_dev->dev.bus_id, "%d.%d", p_dev->socket->sock, p_dev->device_no); p_dev->devname = kmalloc(6 + bus_id_len + 1, GFP_KERNEL); if (!p_dev->devname) goto err_free; sprintf (p_dev->devname, "pcmcia%s", p_dev->dev.bus_id); ds_dbg(3, "devname is %s\n", p_dev->devname); spin_lock_irqsave(&pcmcia_dev_list_lock, flags); /* * p_dev->function_config must be the same for all card functions. * Note that this is serialized by the device_add_lock, so that * only one such struct will be created. */ list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) if (p_dev->func == tmp_dev->func) { p_dev->function_config = tmp_dev->function_config; kref_get(&p_dev->function_config->ref); } /* Add to the list in pcmcia_bus_socket */ list_add(&p_dev->socket_device_list, &s->devices_list); spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); if (!p_dev->function_config) { ds_dbg(3, "creating config_t for %s\n", p_dev->dev.bus_id); p_dev->function_config = kzalloc(sizeof(struct config_t), GFP_KERNEL); if (!p_dev->function_config) goto err_unreg; kref_init(&p_dev->function_config->ref); } printk(KERN_NOTICE "pcmcia: registering new device %s\n", p_dev->devname); pcmcia_device_query(p_dev); if (device_register(&p_dev->dev)) goto err_unreg; mutex_unlock(&device_add_lock); return p_dev; err_unreg: spin_lock_irqsave(&pcmcia_dev_list_lock, flags); list_del(&p_dev->socket_device_list); spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); err_free: kfree(p_dev->devname); kfree(p_dev); s->device_count--; err_put: mutex_unlock(&device_add_lock); pcmcia_put_socket(s); return NULL;}static int pcmcia_card_add(struct pcmcia_socket *s){ cisinfo_t cisinfo; cistpl_longlink_mfc_t mfc; unsigned int no_funcs, i; int ret = 0; if (!(s->resource_setup_done)) { ds_dbg(3, "no resources available, delaying card_add\n"); return -EAGAIN; /* try again, but later... */ } if (pcmcia_validate_mem(s)) { ds_dbg(3, "validating mem resources failed, " "delaying card_add\n"); return -EAGAIN; /* try again, but later... */ } ret = pccard_validate_cis(s, BIND_FN_ALL, &cisinfo); if (ret || !cisinfo.Chains) { ds_dbg(0, "invalid CIS or invalid resources\n"); return -ENODEV; } if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc)) no_funcs = mfc.nfn; else no_funcs = 1; s->functions = no_funcs; for (i=0; i < no_funcs; i++) pcmcia_device_add(s, i); return (ret);}static void pcmcia_delayed_add_device(struct work_struct *work){ struct pcmcia_socket *s = container_of(work, struct pcmcia_socket, device_add); ds_dbg(1, "adding additional device to %d\n", s->sock); pcmcia_device_add(s, s->pcmcia_state.mfc_pfc); s->pcmcia_state.device_add_pending = 0; s->pcmcia_state.mfc_pfc = 0;}static int pcmcia_requery(struct device *dev, void * _data){ struct pcmcia_device *p_dev = to_pcmcia_dev(dev); if (!p_dev->dev.driver) { ds_dbg(1, "update device information for %s\n", p_dev->dev.bus_id); pcmcia_device_query(p_dev); } return 0;}static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis){ int no_devices = 0; int ret = 0; unsigned long flags; /* must be called with skt_mutex held */ ds_dbg(0, "re-scanning socket %d\n", skt->sock); spin_lock_irqsave(&pcmcia_dev_list_lock, flags); if (list_empty(&skt->devices_list)) no_devices = 1; spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); /* If this is because of a CIS override, start over */ if (new_cis && !no_devices) pcmcia_card_remove(skt, NULL); /* if no devices were added for this socket yet because of * missing resource information or other trouble, we need to * do this now. */ if (no_devices || new_cis) { ret = pcmcia_card_add(skt); if (ret) return; } /* some device information might have changed because of a CIS * update or because we can finally read it correctly... so * determine it again, overwriting old values if necessary. */ bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery); /* we re-scan all devices, not just the ones connected to this * socket. This does not matter, though. */ ret = bus_rescan_devices(&pcmcia_bus_type); if (ret) printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n");}#ifdef CONFIG_PCMCIA_LOAD_CIS/** * pcmcia_load_firmware - load CIS from userspace if device-provided is broken * @dev: the pcmcia device which needs a CIS override * @filename: requested filename in /lib/firmware/ * * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if * the one provided by the card is broken. The firmware files reside in * /lib/firmware/ in userspace. */static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename){ struct pcmcia_socket *s = dev->socket; const struct firmware *fw; char path[20]; int ret = -ENOMEM; int no_funcs; int old_funcs; cisdump_t *cis; cistpl_longlink_mfc_t mfc; if (!filename) return -EINVAL; ds_dbg(1, "trying to load CIS file %s\n", filename); if (strlen(filename) > 14) { printk(KERN_WARNING "pcmcia: CIS filename is too long\n"); return -EINVAL; } snprintf(path, 20, "%s", filename); if (request_firmware(&fw, path, &dev->dev) == 0) { if (fw->size >= CISTPL_MAX_CIS_SIZE) { ret = -EINVAL; printk(KERN_ERR "pcmcia: CIS override is too big\n"); goto release; } cis = kzalloc(sizeof(cisdump_t), GFP_KERNEL); if (!cis) { ret = -ENOMEM; goto release; } cis->Length = fw->size + 1; memcpy(cis->Data, fw->data, fw->size); if (!pcmcia_replace_cis(s, cis)) ret = 0; else { printk(KERN_ERR "pcmcia: CIS override failed\n"); goto release; } /* update information */ pcmcia_device_query(dev); /* does this cis override add or remove functions? */ old_funcs = s->functions; if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc)) no_funcs = mfc.nfn; else no_funcs = 1; s->functions = no_funcs; if (old_funcs > no_funcs) pcmcia_card_remove(s, dev); else if (no_funcs > old_funcs) pcmcia_add_device_later(s, 1); } release: release_firmware(fw); return (ret);}#else /* !CONFIG_PCMCIA_LOAD_CIS */static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename){ return -ENODEV;}#endifstatic inline int pcmcia_devmatch(struct pcmcia_device *dev, struct pcmcia_device_id *did){ if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) { if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id)) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID) { if ((!dev->has_card_id) || (dev->card_id != did->card_id)) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION) { if (dev->func != did->function) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1) { if (!dev->prod_id[0]) return 0; if (strcmp(did->prod_id[0], dev->prod_id[0])) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2) { if (!dev->prod_id[1]) return 0; if (strcmp(did->prod_id[1], dev->prod_id[1])) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3) { if (!dev->prod_id[2]) return 0; if (strcmp(did->prod_id[2], dev->prod_id[2])) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4) { if (!dev->prod_id[3]) return 0; if (strcmp(did->prod_id[3], dev->prod_id[3])) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { if (dev->device_no != did->device_no) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { if ((!dev->has_func_id) || (dev->func_id != did->func_id)) return 0; /* if this is a pseudo-multi-function device, * we need explicit matches */ if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) return 0; if (dev->device_no) return 0; /* also, FUNC_ID matching needs to be activated by userspace * after it has re-checked that there is no possible module * with a prod_id/manf_id/card_id match. */ ds_dbg(0, "skipping FUNC_ID match for %s until userspace " "interaction\n", dev->dev.bus_id); if (!dev->allow_func_id_match) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) { ds_dbg(0, "device %s needs a fake CIS\n", dev->dev.bus_id); if (!dev->socket->fake_cis) pcmcia_load_firmware(dev, did->cisfile); if (!dev->socket->fake_cis) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) { int i; for (i=0; i<4; i++) if (dev->prod_id[i]) return 0; if (dev->has_manf_id || dev->has_card_id || dev->has_func_id) return 0; } dev->dev.driver_data = (void *) did; return 1;}static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { struct pcmcia_device * p_dev = to_pcmcia_dev(dev); struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); struct pcmcia_device_id *did = p_drv->id_table; struct pcmcia_dynid *dynid; /* match dynamic devices first */ spin_lock(&p_drv->dynids.lock); list_for_each_entry(dynid, &p_drv->dynids.list, node) { ds_dbg(3, "trying to match %s to %s\n", dev->bus_id, drv->name); if (pcmcia_devmatch(p_dev, &dynid->id)) { ds_dbg(0, "matched %s to %s\n", dev->bus_id, drv->name); spin_unlock(&p_drv->dynids.lock); return 1; } } spin_unlock(&p_drv->dynids.lock);#ifdef CONFIG_PCMCIA_IOCTL /* matching by cardmgr */ if (p_dev->cardmgr == p_drv) { ds_dbg(0, "cardmgr matched %s to %s\n", dev->bus_id, drv->name); return 1; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -