Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ad275e3 authored by Dominik Brodowski's avatar Dominik Brodowski Committed by Linus Torvalds
Browse files

[PATCH] pcmcia: device and driver matching



The actual matching of pcmcia drivers and pcmcia devices.  The original
version of this was written by David Woodhouse.

Signed-off-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 3ee13937
Loading
Loading
Loading
Loading
+122 −1
Original line number Diff line number Diff line
@@ -101,6 +101,9 @@ struct pcmcia_bus_socket {
	u8			device_count; /* the number of devices, used
					       * only internally and subject
					       * to incorrectness and change */

	u8			device_add_pending;
	struct work_struct	device_add;
};
static spinlock_t pcmcia_dev_list_lock;

@@ -512,6 +515,10 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns

	down(&device_add_lock);

	/* max of 2 devices per card */
	if (s->device_count == 2)
		goto err_put;

	p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
	if (!p_dev)
		goto err_put;
@@ -537,6 +544,8 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns
	list_add_tail(&p_dev->socket_device_list, &s->devices_list);
	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);

	pcmcia_device_query(p_dev);

	if (device_register(&p_dev->dev)) {
		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
		list_del(&p_dev->socket_device_list);
@@ -591,14 +600,123 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
}


static void pcmcia_delayed_add_pseudo_device(void *data)
{
	struct pcmcia_bus_socket *s = data;
	pcmcia_device_add(s, 0);
	s->device_add_pending = 0;
}

static inline void pcmcia_add_pseudo_device(struct pcmcia_bus_socket *s)
{
	if (!s->device_add_pending) {
		schedule_work(&s->device_add);
		s->device_add_pending = 1;
	}
	return;
}


static 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) {
		/* handle pseudo multifunction devices:
		 * there are at most two pseudo multifunction devices.
		 * if we're matching against the first, schedule a
		 * call which will then check whether there are two
		 * pseudo devices, and if not, add the second one.
		 */
		if (dev->device_no == 0)
			pcmcia_add_pseudo_device(dev->socket->pcmcia);

		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.
		 */
		if (!dev->allow_func_id_match)
			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;

	/* matching by cardmgr */
	if (p_dev->cardmgr == p_drv)
		return 1;

	while (did && did->match_flags) {
		if (pcmcia_devmatch(p_dev, did))
			return 1;
		did++;
	}

	return 0;
}

@@ -922,7 +1040,9 @@ static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
rescan:
	p_dev->cardmgr = p_drv;

	pcmcia_device_query(p_dev);
	/* if a driver is already running, we can abort */
	if (p_dev->dev.driver)
		goto err_put_module;

	/*
	 * Prevent this racing with a card insertion.
@@ -1595,6 +1715,7 @@ static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev)

	init_waitqueue_head(&s->queue);
	INIT_LIST_HEAD(&s->devices_list);
	INIT_WORK(&s->device_add, pcmcia_delayed_add_pseudo_device, s);

	/* Set up hotline to Card Services */
	s->callback.owner = THIS_MODULE;
+33 −0
Original line number Diff line number Diff line
@@ -175,4 +175,37 @@ struct serio_device_id {
};


/* PCMCIA */

struct pcmcia_device_id {
	__u16		match_flags;

	__u16		manf_id;
	__u16 		card_id;

	__u8  		func_id;

	/* for real multi-function devices */
	__u8  		function;

	/* for pseude multi-function devices */
	__u8  		device_no;

	const char *	prod_id[4];
	__u32 		prod_id_hash[4];

	/* not matched against */
	kernel_ulong_t	driver_info;
};

#define PCMCIA_DEV_ID_MATCH_MANF_ID	0x0001
#define PCMCIA_DEV_ID_MATCH_CARD_ID	0x0002
#define PCMCIA_DEV_ID_MATCH_FUNC_ID	0x0004
#define PCMCIA_DEV_ID_MATCH_FUNCTION	0x0008
#define PCMCIA_DEV_ID_MATCH_PROD_ID1	0x0010
#define PCMCIA_DEV_ID_MATCH_PROD_ID2	0x0020
#define PCMCIA_DEV_ID_MATCH_PROD_ID3	0x0040
#define PCMCIA_DEV_ID_MATCH_PROD_ID4	0x0080
#define PCMCIA_DEV_ID_MATCH_DEVICE_NO	0x0100

#endif /* LINUX_MOD_DEVICETABLE_H */
+175 −0
Original line number Diff line number Diff line
/*
 * Copyright (2003-2004) 	Dominik Brodowski <linux@brodo.de>
 *				David Woodhouse
 *
 * License: GPL v2
 */

#define PCMCIA_DEVICE_MANF_CARD(manf, card) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
			PCMCIA_DEV_ID_MATCH_CARD_ID, \
	.manf_id = (manf), \
	.card_id = (card), }

#define PCMCIA_DEVICE_FUNC_ID(func) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_FUNC_ID, \
	.func_id = (func), }

#define PCMCIA_DEVICE_PROD_ID1(v1, vh1) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1, \
	.prod_id = { (v1), NULL, NULL, NULL }, \
	.prod_id_hash = { (vh1), 0, 0, 0 }, }

#define PCMCIA_DEVICE_PROD_ID2(v2, vh2) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2, \
	.prod_id = { NULL, (v2), NULL, NULL },  \
	.prod_id_hash = { 0, (vh2), 0, 0 }, }

#define PCMCIA_DEVICE_PROD_ID12(v1, v2, vh1, vh2) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2, \
	.prod_id = { (v1), (v2), NULL, NULL }, \
	.prod_id_hash = { (vh1), (vh2), 0, 0 }, }

#define PCMCIA_DEVICE_PROD_ID13(v1, v3, vh1, vh3) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID3, \
	.prod_id = { (v1), NULL, (v3), NULL }, \
	.prod_id_hash = { (vh1), 0, (vh3), 0 }, }

#define PCMCIA_DEVICE_PROD_ID14(v1, v4, vh1, vh4) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID4, \
	.prod_id = { (v1), NULL, NULL, (v4) }, \
	.prod_id_hash = { (vh1), 0, 0, (vh4) }, }

#define PCMCIA_DEVICE_PROD_ID123(v1, v2, v3, vh1, vh2, vh3) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_PROD_ID3, \
	.prod_id = { (v1), (v2), (v3), NULL },\
	.prod_id_hash = { (vh1), (vh2), (vh3), 0 }, }

#define PCMCIA_DEVICE_PROD_ID124(v1, v2, v4, vh1, vh2, vh4) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_PROD_ID4, \
	.prod_id = { (v1), (v2), NULL, (v4) }, \
	.prod_id_hash = { (vh1), (vh2), 0, (vh4) }, }

#define PCMCIA_DEVICE_PROD_ID134(v1, v3, v4, vh1, vh3, vh4) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID3| \
			PCMCIA_DEV_ID_MATCH_PROD_ID4, \
	.prod_id = { (v1), NULL, (v3), (v4) }, \
	.prod_id_hash = { (vh1), 0, (vh3), (vh4) }, }

#define PCMCIA_DEVICE_PROD_ID1234(v1, v2, v3, v4, vh1, vh2, vh3, vh4) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_PROD_ID3| \
			PCMCIA_DEV_ID_MATCH_PROD_ID4, \
	.prod_id = { (v1), (v2), (v3), (v4) }, \
	.prod_id_hash = { (vh1), (vh2), (vh3), (vh4) }, }


/* multi-function devices */

#define PCMCIA_MFC_DEVICE_MANF_CARD(mfc, manf, card) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
			PCMCIA_DEV_ID_MATCH_CARD_ID| \
			PCMCIA_DEV_ID_MATCH_FUNCTION, \
	.manf_id = (manf), \
	.card_id = (card), \
	.function = (mfc), }

#define PCMCIA_MFC_DEVICE_PROD_ID1(mfc, v1, vh1) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_FUNCTION, \
	.prod_id = { (v1), NULL, NULL, NULL }, \
	.prod_id_hash = { (vh1), 0, 0, 0 }, \
	.function = (mfc), }

#define PCMCIA_MFC_DEVICE_PROD_ID2(mfc, v2, vh2) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_FUNCTION, \
	.prod_id = { NULL, (v2), NULL, NULL },  \
	.prod_id_hash = { 0, (vh2), 0, 0 }, \
	.function = (mfc), }

#define PCMCIA_MFC_DEVICE_PROD_ID12(mfc, v1, v2, vh1, vh2) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_FUNCTION, \
	.prod_id = { (v1), (v2), NULL, NULL }, \
	.prod_id_hash = { (vh1), (vh2), 0, 0 }, \
	.function = (mfc), }

#define PCMCIA_MFC_DEVICE_PROD_ID13(mfc, v1, v3, vh1, vh3) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID3| \
			PCMCIA_DEV_ID_MATCH_FUNCTION, \
	.prod_id = { (v1), NULL, (v3), NULL }, \
	.prod_id_hash = { (vh1), 0, (vh3), 0 }, \
	.function = (mfc), }

#define PCMCIA_MFC_DEVICE_PROD_ID123(mfc, v1, v2, v3, vh1, vh2, vh3) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_PROD_ID3| \
			PCMCIA_DEV_ID_MATCH_FUNCTION, \
	.prod_id = { (v1), (v2), (v3), NULL },\
	.prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \
	.function = (mfc), }

/* pseudo multi-function devices */

#define PCMCIA_PFC_DEVICE_MANF_CARD(mfc, manf, card) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
			PCMCIA_DEV_ID_MATCH_CARD_ID| \
			PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
	.manf_id = (manf), \
	.card_id = (card), \
	.device_no = (mfc), }

#define PCMCIA_PFC_DEVICE_PROD_ID1(mfc, v1, vh1) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
	.prod_id = { (v1), NULL, NULL, NULL }, \
	.prod_id_hash = { (vh1), 0, 0, 0 }, \
	.device_no = (mfc), }

#define PCMCIA_PFC_DEVICE_PROD_ID2(mfc, v2, vh2) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
	.prod_id = { NULL, (v2), NULL, NULL },  \
	.prod_id_hash = { 0, (vh2), 0, 0 }, \
	.device_no = (mfc), }

#define PCMCIA_PFC_DEVICE_PROD_ID12(mfc, v1, v2, vh1, vh2) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
	.prod_id = { (v1), (v2), NULL, NULL }, \
	.prod_id_hash = { (vh1), (vh2), 0, 0 }, \
	.device_no = (mfc), }

#define PCMCIA_PFC_DEVICE_PROD_ID13(mfc, v1, v3, vh1, vh3) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID3| \
			PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
	.prod_id = { (v1), NULL, (v3), NULL }, \
	.prod_id_hash = { (vh1), 0, (vh3), 0 }, \
	.device_no = (mfc), }

#define PCMCIA_PFC_DEVICE_PROD_ID123(mfc, v1, v2, v3, vh1, vh2, vh3) { \
	.match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
			PCMCIA_DEV_ID_MATCH_PROD_ID2| \
			PCMCIA_DEV_ID_MATCH_PROD_ID3| \
			PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
	.prod_id = { (v1), (v2), (v3), NULL },\
	.prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \
	.device_no = (mfc), }


#define PCMCIA_DEVICE_NULL { .match_flags = 0, }
+6 −1
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@

#include <pcmcia/bulkmem.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/device_id.h>
#include <linux/mod_devicetable.h>

typedef struct tuple_parse_t {
    tuple_t		tuple;
@@ -135,6 +137,7 @@ struct pcmcia_driver {
	dev_link_t		*(*attach)(void);
	void			(*detach)(dev_link_t *);
	struct module		*owner;
	struct pcmcia_device_id	*id_table;
	struct device_driver	drv;
};

@@ -173,7 +176,9 @@ struct pcmcia_device {
	u8			has_manf_id:1;
	u8			has_card_id:1;
	u8			has_func_id:1;
	u8			reserved:5;

	u8			allow_func_id_match:1;
	u8			reserved:4;

	u8			func_id;
	u16			manf_id;