// SPDX-License-Identifier: GPL-2.0-only /* * ds.c -- 16-bit PCMCIA core support * * The initial developer of the original code is David A. Hinds * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * (C) 1999 David A. Hinds * (C) 2003 - 2010 Dominik Brodowski
*/
/** * new_id_store() - add a new PCMCIA device ID to this driver and re-probe devices * @driver: target device driver * @buf: buffer for scanning device ID data * @count: input size * * Adds a new dynamic PCMCIA device ID to this driver, * and causes the driver to probe for all devices again.
*/ static ssize_t
new_id_store(struct device_driver *driver, constchar *buf, size_t count)
{ struct pcmcia_dynid *dynid; struct pcmcia_driver *pdrv = to_pcmcia_drv(driver);
__u16 match_flags, manf_id, card_id;
__u8 func_id, function, device_no;
__u32 prod_id_hash[4] = {0, 0, 0, 0}; int fields = 0; int retval = 0;
/** * pcmcia_register_driver - register a PCMCIA driver with the bus core * @driver: the &driver being registered * * Registers a PCMCIA driver with the PCMCIA bus core.
*/ int pcmcia_register_driver(struct pcmcia_driver *driver)
{ int error;
staticvoid pcmcia_release_dev(struct device *dev)
{ struct pcmcia_device *p_dev = to_pcmcia_dev(dev); int i;
dev_dbg(dev, "releasing device\n");
pcmcia_put_socket(p_dev->socket); for (i = 0; i < 4; i++)
kfree(p_dev->prod_id[i]);
kfree(p_dev->devname);
kref_put(&p_dev->function_config->ref, pcmcia_release_function);
kfree(p_dev);
}
staticint pcmcia_device_probe(struct device *dev)
{ struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; struct pcmcia_socket *s;
cistpl_config_t cis_config; int ret = 0;
dev = get_device(dev); if (!dev) return -ENODEV;
p_dev = to_pcmcia_dev(dev);
p_drv = to_pcmcia_drv(dev->driver);
s = p_dev->socket;
dev_dbg(dev, "trying to bind to %s\n", p_drv->name);
if ((!p_drv->probe) || (!p_dev->function_config) ||
(!try_module_get(p_drv->owner))) {
ret = -EINVAL; goto put_dev;
}
/* set up some more device information */
ret = pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_CONFIG,
&cis_config); if (!ret) {
p_dev->config_base = cis_config.base;
p_dev->config_regs = cis_config.rmask[0];
dev_dbg(dev, "base %x, regs %x", p_dev->config_base,
p_dev->config_regs);
} else {
dev_info(dev, "pcmcia: could not parse base and rmask0 of CIS\n");
p_dev->config_base = 0;
p_dev->config_regs = 0;
}
/* If we're removing the primary module driving a * pseudo multi-function card, we need to unbind * all devices
*/ if ((p_dev->socket->pcmcia_pfc) &&
(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->remove)
p_drv->remove(p_dev);
/* check for proper unloading */ if (p_dev->_irq || p_dev->_io || p_dev->_locked)
dev_info(dev, "pcmcia: driver %s did not release config properly\n",
p_drv->name);
for (i = 0; i < MAX_WIN; i++) if (p_dev->_win & CLIENT_WIN_REQ(i))
dev_info(dev, "pcmcia: driver %s did not release window properly\n",
p_drv->name);
/* references from pcmcia_device_probe */
pcmcia_put_dev(p_dev);
module_put(p_drv->owner);
}
/* * pcmcia_device_query -- determine information about a pcmcia device
*/ staticint pcmcia_device_query(struct pcmcia_device *p_dev)
{
cistpl_manfid_t manf_id;
cistpl_funcid_t func_id;
cistpl_vers_1_t *vers1; unsignedint i;
vers1 = kmalloc(sizeof(*vers1), GFP_KERNEL); if (!vers1) return -ENOMEM;
/* * p_dev->function_config must be the same for all card functions. * Note that this is serialized by ops_mutex, 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;
p_dev->irq = tmp_dev->irq;
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);
if (pcmcia_setup_irq(p_dev))
dev_warn(&p_dev->dev, "IRQ setup failed -- device might not work\n");
if (!p_dev->function_config) {
config_t *c;
dev_dbg(&p_dev->dev, "creating config_t\n");
c = kzalloc(sizeof(struct config_t), GFP_KERNEL); if (!c) {
mutex_unlock(&s->ops_mutex); goto err_unreg;
}
p_dev->function_config = c;
kref_init(&c->ref); for (i = 0; i < MAX_IO_WIN; i++) {
c->io[i].name = p_dev->devname;
c->io[i].flags = IORESOURCE_IO;
} for (i = 0; i < MAX_WIN; i++) {
c->mem[i].name = p_dev->devname;
c->mem[i].flags = IORESOURCE_MEM;
}
} for (i = 0; i < MAX_IO_WIN; i++)
p_dev->resource[i] = &p_dev->function_config->io[i]; for (; i < (MAX_IO_WIN + MAX_WIN); i++)
p_dev->resource[i] = &p_dev->function_config->mem[i-MAX_IO_WIN];
mutex_unlock(&s->ops_mutex);
dev_notice(&p_dev->dev, "pcmcia: registering new device %s (IRQ: %d)\n",
p_dev->devname, p_dev->irq);
for (i = 0; i < 4; i++)
kfree(p_dev->prod_id[i]);
kfree(p_dev->devname);
kfree(p_dev);
err_put:
pcmcia_put_socket(s);
return NULL;
}
staticint pcmcia_card_add(struct pcmcia_socket *s)
{
cistpl_longlink_mfc_t mfc; unsignedint no_funcs, i, no_chains; int ret = -EAGAIN;
mutex_lock(&s->ops_mutex); if (!(s->resource_setup_done)) {
dev_dbg(&s->dev, "no resources available, delaying card_add\n");
mutex_unlock(&s->ops_mutex); return -EAGAIN; /* try again, but later... */
}
if (pcmcia_validate_mem(s)) {
dev_dbg(&s->dev, "validating mem resources failed, " "delaying card_add\n");
mutex_unlock(&s->ops_mutex); return -EAGAIN; /* try again, but later... */
}
mutex_unlock(&s->ops_mutex);
ret = pccard_validate_cis(s, &no_chains); if (ret || !no_chains) { #ifdefined(CONFIG_MTD_PCMCIA_ANONYMOUS) /* Set up as an anonymous card. If we don't have anonymous memory support then just error the card as there is no point trying to second guess.
Note: some cards have just a device entry, it may be
worth extending support to cover these in future */ if (ret == -EIO) {
dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n");
pcmcia_replace_cis(s, "\xFF", 1);
no_chains = 1;
ret = 0;
} else #endif
{
dev_dbg(&s->dev, "invalid CIS or invalid resources\n"); return -ENODEV;
}
}
staticvoid pcmcia_requery(struct pcmcia_socket *s)
{ int has_pfc;
if (!(s->state & SOCKET_PRESENT)) return;
if (s->functions == 0) {
pcmcia_card_add(s); 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_callback);
/* if the CIS changed, we need to check whether the number of
* functions changed. */ if (s->fake_cis) { int old_funcs, new_funcs;
cistpl_longlink_mfc_t mfc;
/* does this cis override add or remove functions? */
old_funcs = s->functions;
if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
&mfc))
new_funcs = mfc.nfn; else
new_funcs = 1; if (old_funcs != new_funcs) { /* we need to re-start */
pcmcia_card_remove(s, NULL);
s->functions = 0;
pcmcia_card_add(s);
}
}
/* If the PCMCIA device consists of two pseudo devices, * call pcmcia_device_add() -- which will fail if both
* devices are already registered. */
mutex_lock(&s->ops_mutex);
has_pfc = s->pcmcia_pfc;
mutex_unlock(&s->ops_mutex); if (has_pfc)
pcmcia_device_add(s, 0);
/* we re-scan all devices, not just the ones connected to this
* socket. This does not matter, though. */ if (bus_rescan_devices(&pcmcia_bus_type))
dev_warn(&s->dev, "rescanning the bus 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.
*/ staticint pcmcia_load_firmware(struct pcmcia_device *dev, char *filename)
{ struct pcmcia_socket *s = dev->socket; conststruct firmware *fw; int ret = -ENOMEM;
cistpl_longlink_mfc_t mfc; int old_funcs, new_funcs = 1;
if (!filename) return -EINVAL;
dev_dbg(&dev->dev, "trying to load CIS file %s\n", filename);
if (request_firmware(&fw, filename, &dev->dev) == 0) { if (fw->size >= CISTPL_MAX_CIS_SIZE) {
ret = -EINVAL;
dev_err(&dev->dev, "pcmcia: CIS override is too big\n"); goto release;
}
if (!pcmcia_replace_cis(s, fw->data, fw->size))
ret = 0; else {
dev_err(&dev->dev, "pcmcia: CIS override failed\n"); goto release;
}
/* we need to re-start if the number of functions changed */
old_funcs = s->functions; if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
&mfc))
new_funcs = mfc.nfn;
if (old_funcs != new_funcs)
ret = -EBUSY;
/* update information */
pcmcia_device_query(dev);
/* requery (as number of functions might have changed) */
pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
}
release:
release_firmware(fw);
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) {
dev_dbg(&dev->dev, "this is a pseudo-multi-function device\n");
mutex_lock(&dev->socket->ops_mutex);
dev->socket->pcmcia_pfc = 1;
mutex_unlock(&dev->socket->ops_mutex); if (dev->device_no != did->device_no) return 0;
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { int ret;
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 (dev->socket->pcmcia_pfc) 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.
*/
mutex_lock(&dev->socket->ops_mutex);
ret = dev->allow_func_id_match;
mutex_unlock(&dev->socket->ops_mutex);
if (!ret) {
dev_dbg(&dev->dev, "skipping FUNC_ID match until userspace ACK\n"); return 0;
}
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
dev_dbg(&dev->dev, "device needs a fake CIS\n"); if (!dev->socket->fake_cis) if (pcmcia_load_firmware(dev, did->cisfile)) 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;
}
/* match dynamic devices first */
mutex_lock(&p_drv->dynids.lock);
list_for_each_entry(dynid, &p_drv->dynids.list, node) {
dev_dbg(dev, "trying to match to %s\n", drv->name); if (pcmcia_devmatch(p_dev, &dynid->id)) {
dev_dbg(dev, "matched to %s\n", drv->name);
mutex_unlock(&p_drv->dynids.lock); return 1;
}
}
mutex_unlock(&p_drv->dynids.lock);
while (did && did->match_flags) {
dev_dbg(dev, "trying to match to %s\n", drv->name); if (pcmcia_devmatch(p_dev, did)) {
dev_dbg(dev, "matched to %s\n", drv->name); return 1;
}
did++;
}
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
if (!p_drv) goto out;
if (p_drv->suspend) {
ret = p_drv->suspend(p_dev); if (ret) {
dev_err(dev, "pcmcia: device %s (driver %s) did not want to go to sleep (%d)\n",
p_dev->devname, p_drv->name, ret);
mutex_lock(&p_dev->socket->ops_mutex);
p_dev->suspended = 0;
mutex_unlock(&p_dev->socket->ops_mutex); goto out;
}
}
if (p_dev->device_no == p_dev->func) {
dev_dbg(dev, "releasing configuration\n");
pcmcia_release_configuration(p_dev);
}
out: return ret;
}
staticint pcmcia_dev_resume(struct device *dev)
{ struct pcmcia_device *p_dev = to_pcmcia_dev(dev); struct pcmcia_driver *p_drv = NULL; int ret = 0;
/* now, add the new card */
pcmcia_bus_add(skt); return 0;
}
/* * NOTE: This is racy. There's no guarantee the card will still be * physically present, even if the call to this function returns * non-NULL. Furthermore, the device driver most likely is unbound * almost immediately, so the timeframe where pcmcia_dev_present * returns NULL is probably really, really small.
*/ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
{ struct pcmcia_device *p_dev; struct pcmcia_device *ret = NULL;
p_dev = pcmcia_get_dev(_p_dev); if (!p_dev) return NULL;
if (atomic_read(&p_dev->socket->present) != 0)
ret = p_dev;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.