Пример #1
0
static int ds_event(event_t event, int priority,
                    event_callback_args_t *args)
{
    struct pcmcia_bus_socket *s;

    ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n",
           event, priority, args->client_handle);
    s = args->client_data;

    switch (event) {

    case CS_EVENT_CARD_REMOVAL:
        s->state &= ~DS_SOCKET_PRESENT;
        if (!(s->state & DS_SOCKET_REMOVAL_PENDING)) {
            s->state |= DS_SOCKET_REMOVAL_PENDING;
            schedule_delayed_work(&s->removal,  HZ/10);
        }
        break;

    case CS_EVENT_CARD_INSERTION:
        s->state |= DS_SOCKET_PRESENT;
        handle_event(s, event);
        break;

    case CS_EVENT_EJECTION_REQUEST:
        return handle_request(s, event);
        break;

    default:
        handle_event(s, event);
        break;
    }

    return 0;
} /* ds_event */
Пример #2
0
static void pcmcia_release_dev(struct device *dev)
{
	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
	ds_dbg(1, "releasing dev %p\n", p_dev);
	pcmcia_put_bus_socket(p_dev->socket->pcmcia);
	kfree(p_dev);
}
Пример #3
0
static ssize_t ds_read(struct file *file, char __user *buf,
                       size_t count, loff_t *ppos)
{
    struct pcmcia_bus_socket *s;
    user_info_t *user;
    int ret;

    ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode));

    if (count < 4)
        return -EINVAL;

    user = file->private_data;
    if (CHECK_USER(user))
        return -EIO;

    s = user->socket;
    if (s->state & DS_SOCKET_DEAD)
        return -EIO;

    ret = wait_event_interruptible(s->queue, !queue_empty(user));
    if (ret == 0)
        ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;

    return ret;
} /* ds_read */
Пример #4
0
static ssize_t ds_write(struct file *file, const char __user *buf,
                        size_t count, loff_t *ppos)
{
    struct pcmcia_bus_socket *s;
    user_info_t *user;

    ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_dentry->d_inode));

    if (count != 4)
        return -EINVAL;
    if ((file->f_flags & O_ACCMODE) == O_RDONLY)
        return -EBADF;

    user = file->private_data;
    if (CHECK_USER(user))
        return -EIO;

    s = user->socket;
    if (s->state & DS_SOCKET_DEAD)
        return -EIO;

    if (s->req_pending) {
        s->req_pending--;
        get_user(s->req_result, (int __user *)buf);
        if ((s->req_result != 0) || (s->req_pending == 0))
            wake_up_interruptible(&s->request);
    } else
        return -EIO;

    return 4;
} /* ds_write */
Пример #5
0
static int ds_open(struct inode *inode, struct file *file)
{
    socket_t i = iminor(inode);
    struct pcmcia_bus_socket *s;
    user_info_t *user;

    ds_dbg(0, "ds_open(socket %d)\n", i);

    s = pcmcia_get_bus_socket(i);
    if (!s)
        return -ENODEV;

    if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
        if (s->state & DS_SOCKET_BUSY)
            return -EBUSY;
        else
            s->state |= DS_SOCKET_BUSY;
    }

    user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
    if (!user) return -ENOMEM;
    user->event_tail = user->event_head = 0;
    user->next = s->user;
    user->user_magic = USER_MAGIC;
    user->socket = s;
    s->user = user;
    file->private_data = user;

    if (s->state & DS_SOCKET_PRESENT)
        queue_event(user, CS_EVENT_CARD_INSERTION);
    return 0;
} /* ds_open */
Пример #6
0
static int ds_release(struct inode *inode, struct file *file)
{
    struct pcmcia_bus_socket *s;
    user_info_t *user, **link;

    ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));

    user = file->private_data;
    if (CHECK_USER(user))
        goto out;

    s = user->socket;

    /* Unlink user data structure */
    if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
        s->state &= ~DS_SOCKET_BUSY;
        s->req_pending = 0;
        wake_up_interruptible(&s->request);
    }
    file->private_data = NULL;
    for (link = &s->user; *link; link = &(*link)->next)
        if (*link == user) break;
    if (link == NULL)
        goto out;
    *link = user->next;
    user->user_magic = 0;
    kfree(user);
    pcmcia_put_bus_socket(s);
out:
    return 0;
} /* ds_release */
Пример #7
0
static int pcmcia_bind_mtd(mtd_bind_t *req)
{
    struct pcmcia_socket *s;
    memory_handle_t region;

    s = req->Socket;
    if (!s)
        return CS_BAD_SOCKET;

    if (req->Attributes & REGION_TYPE_AM)
        region = s->a_region;
    else
        region = s->c_region;

    while (region) {
        if (region->info.CardOffset == req->CardOffset)
            break;
        region = region->info.next;
    }
    if (!region || (region->mtd != NULL))
        return CS_BAD_OFFSET;
    strlcpy(region->dev_info, (char *)req->dev_info, DEV_NAME_LEN);

    ds_dbg(1, "%s: bind_mtd: attr 0x%x, offset 0x%x, dev %s\n",
           cs_socket_name(s), req->Attributes, req->CardOffset,
           (char *)req->dev_info);
    return CS_SUCCESS;
} /* bind_mtd */
Пример #8
0
static int unbind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
{
    socket_bind_t **b, *c;

    ds_dbg(2, "unbind_request(%d, '%s')\n", s->parent->sock,
           (char *)bind_info->dev_info);
    for (b = &s->bind; *b; b = &(*b)->next)
        if ((strcmp((char *)(*b)->driver->drv.name,
                    (char *)bind_info->dev_info) == 0) &&
                ((*b)->function == bind_info->function))
            break;
    if (*b == NULL)
        return -ENODEV;

    c = *b;
    c->driver->use_count--;
    if (c->driver->detach) {
        if (c->instance)
            c->driver->detach(c->instance);
    }
    module_put(c->driver->owner);
    *b = c->next;
    kfree(c);
    return 0;
} /* unbind_request */
Пример #9
0
static int pcmcia_bind_device(bind_req_t *req)
{
    client_t *client;
    struct pcmcia_socket *s;

    s = req->Socket;
    if (!s)
        return CS_BAD_SOCKET;

    client = (client_t *) kmalloc(sizeof(client_t), GFP_KERNEL);
    if (!client)
        return CS_OUT_OF_RESOURCE;
    memset(client, '\0', sizeof(client_t));
    client->client_magic = CLIENT_MAGIC;
    strlcpy(client->dev_info, (char *)req->dev_info, DEV_NAME_LEN);
    client->Socket = s;
    client->Function = req->Function;
    client->state = CLIENT_UNBOUND;
    client->erase_busy.next = &client->erase_busy;
    client->erase_busy.prev = &client->erase_busy;
    init_waitqueue_head(&client->mtd_req);
    client->next = s->clients;
    s->clients = client;
    ds_dbg(1, "%s: bind_device(): client 0x%p, dev %s\n",
           cs_socket_name(client->Socket), client, client->dev_info);
    return CS_SUCCESS;
} /* bind_device */
Пример #10
0
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))
		return -EAGAIN; /* try again, but later... */

	if (pcmcia_validate_mem(s))
		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;

	for (i=0; i < no_funcs; i++)
		pcmcia_device_add(s, i);

	return (ret);
}
Пример #11
0
/*
 * Removes a PCMCIA card from the device tree and socket list.
 */
static void pcmcia_card_remove(struct pcmcia_socket *s)
{
	struct pcmcia_device	*p_dev;
	unsigned long		flags;

	ds_dbg(2, "unbind_request(%d)\n", s->sock);

	s->device_count = 0;

	for (;;) {
		/* unregister all pcmcia_devices registered with this socket*/
		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
		if (list_empty(&s->devices_list)) {
			spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
 			return;
		}
		p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list);
		list_del(&p_dev->socket_device_list);
		p_dev->state |= CLIENT_STALE;
		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);

		device_unregister(&p_dev->dev);
	}

	return;
} /* unbind_request */
Пример #12
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;

	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;
		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;
		}
	}

	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 < 3) || (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);
		}
	}

	return 0;
}
Пример #13
0
/* No kernel lock - fine */
static u_int ds_poll(struct file *file, poll_table *wait)
{
    struct pcmcia_bus_socket *s;
    user_info_t *user;

    ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode));

    user = file->private_data;
    if (CHECK_USER(user))
        return POLLERR;
    s = user->socket;
    /*
     * We don't check for a dead socket here since that
     * will send cardmgr into an endless spin.
     */
    poll_wait(file, &s->queue, wait);
    if (!queue_empty(user))
        return POLLIN | POLLRDNORM;
    return 0;
} /* ds_poll */
Пример #14
0
/**
 * 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/cis/
 *
 * 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/cis/ 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;
	cisdump_t *cis;

	if (!filename)
		return -EINVAL;

	ds_dbg(1, "trying to load firmware %s\n", filename);

	if (strlen(filename) > 14)
		return -EINVAL;

	snprintf(path, 20, "%s", filename);

	if (request_firmware(&fw, path, &dev->dev) == 0) {
		if (fw->size >= CISTPL_MAX_CIS_SIZE)
			goto release;

		cis = kzalloc(sizeof(cisdump_t), GFP_KERNEL);
		if (!cis)
			goto release;

		cis->Length = fw->size + 1;
		memcpy(cis->Data, fw->data, fw->size);

		if (!pcmcia_replace_cis(s, cis))
			ret = 0;
	}
 release:
	release_firmware(fw);

	return (ret);
}
Пример #15
0
static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
{
	struct pcmcia_socket *s = pcmcia_get_socket(skt);

	ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n",
	       event, priority, skt);

	switch (event) {
	case CS_EVENT_CARD_REMOVAL:
		s->pcmcia_state.present = 0;
		pcmcia_card_remove(skt);
		handle_event(skt, event);
		break;

	case CS_EVENT_CARD_INSERTION:
		s->pcmcia_state.present = 1;
		pcmcia_card_add(skt);
		handle_event(skt, event);
		break;

	case CS_EVENT_EJECTION_REQUEST:
		break;

	case CS_EVENT_PM_SUSPEND:
	case CS_EVENT_PM_RESUME:
	case CS_EVENT_RESET_PHYSICAL:
	case CS_EVENT_CARD_RESET:
	default:
		handle_event(skt, event);
		break;
    }

    pcmcia_put_socket(s);

    return 0;
} /* ds_event */
Пример #16
0
static int ds_ioctl(struct inode * inode, struct file * file,
                    u_int cmd, u_long arg)
{
    struct pcmcia_bus_socket *s;
    void __user *uarg = (char __user *)arg;
    u_int size;
    int ret, err;
    ds_ioctl_arg_t buf;
    user_info_t *user;

    ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);

    user = file->private_data;
    if (CHECK_USER(user))
        return -EIO;

    s = user->socket;
    if (s->state & DS_SOCKET_DEAD)
        return -EIO;

    size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
    if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;

    /* Permission check */
    if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
        return -EPERM;

    if (cmd & IOC_IN) {
        err = verify_area(VERIFY_READ, uarg, size);
        if (err) {
            ds_dbg(3, "ds_ioctl(): verify_read = %d\n", err);
            return err;
        }
    }
    if (cmd & IOC_OUT) {
        err = verify_area(VERIFY_WRITE, uarg, size);
        if (err) {
            ds_dbg(3, "ds_ioctl(): verify_write = %d\n", err);
            return err;
        }
    }

    err = ret = 0;

    if (cmd & IOC_IN) __copy_from_user((char *)&buf, uarg, size);

    switch (cmd) {
    case DS_ADJUST_RESOURCE_INFO:
        ret = pcmcia_adjust_resource_info(s->handle, &buf.adjust);
        break;
    case DS_GET_CARD_SERVICES_INFO:
        ret = pcmcia_get_card_services_info(&buf.servinfo);
        break;
    case DS_GET_CONFIGURATION_INFO:
        ret = pcmcia_get_configuration_info(s->handle, &buf.config);
        break;
    case DS_GET_FIRST_TUPLE:
        ret = pcmcia_get_first_tuple(s->handle, &buf.tuple);
        break;
    case DS_GET_NEXT_TUPLE:
        ret = pcmcia_get_next_tuple(s->handle, &buf.tuple);
        break;
    case DS_GET_TUPLE_DATA:
        buf.tuple.TupleData = buf.tuple_parse.data;
        buf.tuple.TupleDataMax = sizeof(buf.tuple_parse.data);
        ret = pcmcia_get_tuple_data(s->handle, &buf.tuple);
        break;
    case DS_PARSE_TUPLE:
        buf.tuple.TupleData = buf.tuple_parse.data;
        ret = pcmcia_parse_tuple(s->handle, &buf.tuple, &buf.tuple_parse.parse);
        break;
    case DS_RESET_CARD:
        ret = pcmcia_reset_card(s->handle, NULL);
        break;
    case DS_GET_STATUS:
        ret = pcmcia_get_status(s->handle, &buf.status);
        break;
    case DS_VALIDATE_CIS:
        ret = pcmcia_validate_cis(s->handle, &buf.cisinfo);
        break;
    case DS_SUSPEND_CARD:
        ret = pcmcia_suspend_card(s->parent);
        break;
    case DS_RESUME_CARD:
        ret = pcmcia_resume_card(s->parent);
        break;
    case DS_EJECT_CARD:
        ret = pcmcia_eject_card(s->parent);
        break;
    case DS_INSERT_CARD:
        ret = pcmcia_insert_card(s->parent);
        break;
    case DS_ACCESS_CONFIGURATION_REGISTER:
        if ((buf.conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN))
            return -EPERM;
        ret = pcmcia_access_configuration_register(s->handle, &buf.conf_reg);
        break;
    case DS_GET_FIRST_REGION:
        ret = pcmcia_get_first_region(s->handle, &buf.region);
        break;
    case DS_GET_NEXT_REGION:
        ret = pcmcia_get_next_region(s->handle, &buf.region);
        break;
    case DS_GET_FIRST_WINDOW:
        buf.win_info.handle = (window_handle_t)s->handle;
        ret = pcmcia_get_first_window(&buf.win_info.handle, &buf.win_info.window);
        break;
    case DS_GET_NEXT_WINDOW:
        ret = pcmcia_get_next_window(&buf.win_info.handle, &buf.win_info.window);
        break;
    case DS_GET_MEM_PAGE:
        ret = pcmcia_get_mem_page(buf.win_info.handle,
                                  &buf.win_info.map);
        break;
    case DS_REPLACE_CIS:
        ret = pcmcia_replace_cis(s->handle, &buf.cisdump);
        break;
    case DS_BIND_REQUEST:
        if (!capable(CAP_SYS_ADMIN)) return -EPERM;
        err = bind_request(s, &buf.bind_info);
        break;
    case DS_GET_DEVICE_INFO:
        err = get_device_info(s, &buf.bind_info, 1);
        break;
    case DS_GET_NEXT_DEVICE:
        err = get_device_info(s, &buf.bind_info, 0);
        break;
    case DS_UNBIND_REQUEST:
        err = unbind_request(s, &buf.bind_info);
        break;
    case DS_BIND_MTD:
        if (!capable(CAP_SYS_ADMIN)) return -EPERM;
        err = bind_mtd(s, &buf.mtd_info);
        break;
    default:
        err = -EINVAL;
    }

    if ((err == 0) && (ret != CS_SUCCESS)) {
        ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
        switch (ret) {
        case CS_BAD_SOCKET:
        case CS_NO_CARD:
            err = -ENODEV;
            break;
        case CS_BAD_ARGS:
        case CS_BAD_ATTRIBUTE:
        case CS_BAD_IRQ:
        case CS_BAD_TUPLE:
            err = -EINVAL;
            break;
        case CS_IN_USE:
            err = -EBUSY;
            break;
        case CS_OUT_OF_RESOURCE:
            err = -ENOSPC;
            break;
        case CS_NO_MORE_ITEMS:
            err = -ENODATA;
            break;
        case CS_UNSUPPORTED_FUNCTION:
            err = -ENOSYS;
            break;
        default:
            err = -EIO;
            break;
        }
    }

    if (cmd & IOC_OUT) __copy_to_user(uarg, (char *)&buf, size);

    return err;
} /* ds_ioctl */
Пример #17
0
static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
{
    struct pcmcia_driver *driver;
    socket_bind_t *b;
    bind_req_t bind_req;
    int ret;

    if (!s)
        return -EINVAL;

    ds_dbg(2, "bind_request(%d, '%s')\n", s->parent->sock,
           (char *)bind_info->dev_info);
    driver = get_pcmcia_driver(&bind_info->dev_info);
    if (!driver)
        return -EINVAL;

    for (b = s->bind; b; b = b->next)
        if ((driver == b->driver) &&
                (bind_info->function == b->function))
            break;
    if (b != NULL) {
        bind_info->instance = b->instance;
        return -EBUSY;
    }

    if (!try_module_get(driver->owner))
        return -EINVAL;

    bind_req.Socket = s->parent;
    bind_req.Function = bind_info->function;
    bind_req.dev_info = (dev_info_t *) driver->drv.name;
    ret = pcmcia_bind_device(&bind_req);
    if (ret != CS_SUCCESS) {
        cs_error(NULL, BindDevice, ret);
        printk(KERN_NOTICE "ds: unable to bind '%s' to socket %d\n",
               (char *)dev_info, s->parent->sock);
        module_put(driver->owner);
        return -ENODEV;
    }

    /* Add binding to list for this socket */
    driver->use_count++;
    b = kmalloc(sizeof(socket_bind_t), GFP_KERNEL);
    if (!b)
    {
        driver->use_count--;
        module_put(driver->owner);
        return -ENOMEM;
    }
    b->driver = driver;
    b->function = bind_info->function;
    b->instance = NULL;
    b->next = s->bind;
    s->bind = b;

    if (driver->attach) {
        b->instance = driver->attach();
        if (b->instance == NULL) {
            printk(KERN_NOTICE "ds: unable to create instance "
                   "of '%s'!\n", (char *)bind_info->dev_info);
            module_put(driver->owner);
            return -ENODEV;
        }
    }

    return 0;
} /* bind_request */