xref: /minix3/minix/lib/libdevman/generic.c (revision e1cdaee10649323af446eb1a74571984b2ab3181)
1433d6423SLionel Sambuc #include <errno.h>
2433d6423SLionel Sambuc #include <stdlib.h>
3433d6423SLionel Sambuc #include <string.h>
4433d6423SLionel Sambuc #include <sys/queue.h>
5433d6423SLionel Sambuc #include <minix/com.h>
6433d6423SLionel Sambuc #include <minix/ipc.h>
7433d6423SLionel Sambuc #include <minix/const.h>
8433d6423SLionel Sambuc #include <minix/devman.h>
9433d6423SLionel Sambuc #include <minix/safecopies.h>
10433d6423SLionel Sambuc #include <minix/sysutil.h>
11433d6423SLionel Sambuc #include <minix/ds.h>
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc #include "local.h"
14433d6423SLionel Sambuc 
15433d6423SLionel Sambuc static endpoint_t devman_ep;
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc static int save_string(char *buffer, char *src, size_t *offset);
18433d6423SLionel Sambuc 
19433d6423SLionel Sambuc static TAILQ_HEAD(devlist_head, devman_dev) dev_list;
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc /****************************************************************************
22433d6423SLionel Sambuc  *     save_string                                                          *
23433d6423SLionel Sambuc  ***************************************************************************/
save_string(char * buffer,char * src,size_t * offset)24433d6423SLionel Sambuc static int save_string(char *buffer, char *src, size_t *offset)
25433d6423SLionel Sambuc {
26433d6423SLionel Sambuc 	unsigned old_offset = *offset;
27433d6423SLionel Sambuc 	size_t len = strlen(src) + 1;
28433d6423SLionel Sambuc 	memcpy(buffer + *offset, src, len);
29433d6423SLionel Sambuc 	*offset += len;
30433d6423SLionel Sambuc 	return old_offset;
31433d6423SLionel Sambuc }
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc /****************************************************************************
34433d6423SLionel Sambuc  *     serialize_dev                                                        *
35433d6423SLionel Sambuc  ***************************************************************************/
serialize_dev(struct devman_dev * dev,size_t * overall_size)3665f76edbSDavid van Moolenbroek static void *serialize_dev(struct devman_dev *dev, size_t *overall_size)
37433d6423SLionel Sambuc {
38433d6423SLionel Sambuc 	/* determine size of serialized version of dev */
39433d6423SLionel Sambuc 	char *buffer;
40433d6423SLionel Sambuc 	char *string_buffer;
41433d6423SLionel Sambuc 	size_t string_buffer_offset;
42433d6423SLionel Sambuc 	size_t count = 0;
43433d6423SLionel Sambuc 	size_t size = sizeof(struct devman_device_info);
44433d6423SLionel Sambuc 	size_t strings_size = strlen(dev->name) + 1;
45433d6423SLionel Sambuc 	struct devman_device_info * serialized_dev;
46433d6423SLionel Sambuc 	struct devman_device_info_entry *entry;
47433d6423SLionel Sambuc 	struct devman_static_attribute *attribute;
48433d6423SLionel Sambuc 
49433d6423SLionel Sambuc 	TAILQ_FOREACH(attribute, &dev->attrs, list) {
50433d6423SLionel Sambuc 		strings_size += strlen(attribute->name) + 1;
51433d6423SLionel Sambuc 		strings_size += strlen(attribute->data) + 1;
52433d6423SLionel Sambuc 		size += sizeof(struct devman_device_info_entry);
53433d6423SLionel Sambuc 		count++;
54433d6423SLionel Sambuc 	}
55433d6423SLionel Sambuc 
56433d6423SLionel Sambuc 	buffer = malloc(size + strings_size);
57433d6423SLionel Sambuc 
58433d6423SLionel Sambuc 	if (buffer == NULL) {
59433d6423SLionel Sambuc 		return NULL;
60433d6423SLionel Sambuc 	}
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc 	string_buffer = buffer;
63433d6423SLionel Sambuc 	string_buffer_offset = size; /* strings start after
64433d6423SLionel Sambuc                                     devman_device_info and
65433d6423SLionel Sambuc 							   devman_device_info_entries */
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc 	/* serialize device */
68433d6423SLionel Sambuc 	serialized_dev = (struct devman_device_info *) buffer;
69433d6423SLionel Sambuc 
70433d6423SLionel Sambuc 	serialized_dev->count         = count;
71433d6423SLionel Sambuc 	serialized_dev->parent_dev_id = dev->parent_dev_id;
72433d6423SLionel Sambuc 	serialized_dev->name_offset   =
73433d6423SLionel Sambuc 	    save_string(string_buffer, dev->name, &string_buffer_offset);
74433d6423SLionel Sambuc #if 0
75433d6423SLionel Sambuc 	serialized_dev->bus           =
76433d6423SLionel Sambuc 	    save_string(string_buffer, dev->bus, &string_buffer_offset);
77433d6423SLionel Sambuc #endif
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc 	/* serialize entries */
80433d6423SLionel Sambuc 	entry =
81433d6423SLionel Sambuc 	    (struct devman_device_info_entry *)
82433d6423SLionel Sambuc 	    (buffer + sizeof(struct devman_device_info));
83433d6423SLionel Sambuc 
84433d6423SLionel Sambuc 	TAILQ_FOREACH(attribute, &dev->attrs, list) {
85433d6423SLionel Sambuc 		entry->type = 0; /* TODO: use macro */
86433d6423SLionel Sambuc 		entry->name_offset =
87433d6423SLionel Sambuc 		    save_string(string_buffer, attribute->name, &string_buffer_offset);
88433d6423SLionel Sambuc 		entry->data_offset =
89433d6423SLionel Sambuc 		    save_string(string_buffer, attribute->data, &string_buffer_offset);
90433d6423SLionel Sambuc 		entry++;
91433d6423SLionel Sambuc 	}
92433d6423SLionel Sambuc 
93433d6423SLionel Sambuc 	*overall_size = size + strings_size;
94433d6423SLionel Sambuc 
95433d6423SLionel Sambuc 	return buffer;
96433d6423SLionel Sambuc 
97433d6423SLionel Sambuc }
98433d6423SLionel Sambuc 
99433d6423SLionel Sambuc /****************************************************************************
100433d6423SLionel Sambuc  *     devman_add_device                                                    *
101433d6423SLionel Sambuc  ***************************************************************************/
devman_add_device(struct devman_dev * dev)102433d6423SLionel Sambuc int devman_add_device(struct devman_dev *dev)
103433d6423SLionel Sambuc {
104433d6423SLionel Sambuc 	message msg;
105433d6423SLionel Sambuc 	int res;
106*e1cdaee1SLionel Sambuc 	size_t grant_size = 0;
107433d6423SLionel Sambuc 	void *buf = serialize_dev(dev, &grant_size);
108433d6423SLionel Sambuc 
1097c48de6cSDavid van Moolenbroek 	if (buf == NULL)
1107c48de6cSDavid van Moolenbroek 		panic("out of memory");
1117c48de6cSDavid van Moolenbroek 
112433d6423SLionel Sambuc 	cp_grant_id_t gid =
113433d6423SLionel Sambuc 	    cpf_grant_direct(devman_ep,(vir_bytes) buf,
114433d6423SLionel Sambuc 		    grant_size, CPF_READ);
115433d6423SLionel Sambuc 
116433d6423SLionel Sambuc 	/* prepare message */
117433d6423SLionel Sambuc 	msg.m_type            = DEVMAN_ADD_DEV;
118433d6423SLionel Sambuc 	msg.DEVMAN_GRANT_ID   = gid;
119433d6423SLionel Sambuc 	msg.DEVMAN_GRANT_SIZE = grant_size;
120433d6423SLionel Sambuc 
121433d6423SLionel Sambuc 	/* send message */
122433d6423SLionel Sambuc 	res = ipc_sendrec(devman_ep, &msg);
123433d6423SLionel Sambuc 
124433d6423SLionel Sambuc 	if (res != 0) {
125433d6423SLionel Sambuc 		panic("devman_add_device: could not talk to devman: %d", res);
126433d6423SLionel Sambuc 	}
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc 	if (msg.m_type != DEVMAN_REPLY) {
129433d6423SLionel Sambuc 		panic("devman_add_device: got illegal response from devman: %d",
130433d6423SLionel Sambuc 		    msg.m_type);
131433d6423SLionel Sambuc 	}
132433d6423SLionel Sambuc 
133433d6423SLionel Sambuc 	if (msg.DEVMAN_RESULT != 0) {
134433d6423SLionel Sambuc 		panic("devman_add_device: could add device: %ld",
135433d6423SLionel Sambuc 		    msg.DEVMAN_RESULT);
136433d6423SLionel Sambuc 	}
137433d6423SLionel Sambuc 
138433d6423SLionel Sambuc 	/* store given dev_id to dev */
139433d6423SLionel Sambuc 	dev->dev_id = msg.DEVMAN_DEVICE_ID;
140433d6423SLionel Sambuc 
141433d6423SLionel Sambuc 	cpf_revoke(gid);
142433d6423SLionel Sambuc 
143433d6423SLionel Sambuc 	free(buf);
144433d6423SLionel Sambuc 
145433d6423SLionel Sambuc 	/* put device in list */
146433d6423SLionel Sambuc 	TAILQ_INSERT_HEAD(&dev_list, dev, dev_list);
147433d6423SLionel Sambuc 
148433d6423SLionel Sambuc 	return 0;
149433d6423SLionel Sambuc }
150433d6423SLionel Sambuc 
151433d6423SLionel Sambuc /****************************************************************************
152433d6423SLionel Sambuc  *     devman_del_device                                                    *
153433d6423SLionel Sambuc  ***************************************************************************/
devman_del_device(struct devman_dev * dev)154433d6423SLionel Sambuc int devman_del_device(struct devman_dev *dev)
155433d6423SLionel Sambuc {
156433d6423SLionel Sambuc 	message msg;
157433d6423SLionel Sambuc 	int res;
158433d6423SLionel Sambuc 
159433d6423SLionel Sambuc 	msg.m_type            = DEVMAN_DEL_DEV;
160433d6423SLionel Sambuc 	msg.DEVMAN_DEVICE_ID   = dev->dev_id;
161433d6423SLionel Sambuc 
162433d6423SLionel Sambuc 	res = ipc_sendrec(devman_ep, &msg);
163433d6423SLionel Sambuc 
164433d6423SLionel Sambuc 	if (res != 0) {
165433d6423SLionel Sambuc 		panic("devman_del_device: could not talk to devman: %d", res);
166433d6423SLionel Sambuc 	}
167433d6423SLionel Sambuc 
168433d6423SLionel Sambuc 	if (msg.m_type != DEVMAN_REPLY) {
169433d6423SLionel Sambuc 		panic("devman_del_device: got illegal response from devman: %d",
170433d6423SLionel Sambuc 		    msg.m_type);
171433d6423SLionel Sambuc 	}
172433d6423SLionel Sambuc 
173433d6423SLionel Sambuc 	if (msg.DEVMAN_RESULT != 0) {
174433d6423SLionel Sambuc 		panic("devman_del_device: could delete device: %ld",
175433d6423SLionel Sambuc 		    msg.DEVMAN_RESULT);
176433d6423SLionel Sambuc 	}
177433d6423SLionel Sambuc 
178433d6423SLionel Sambuc 	/* remove the device from list */
179433d6423SLionel Sambuc 	TAILQ_REMOVE(&dev_list, dev, dev_list);
180433d6423SLionel Sambuc 
181433d6423SLionel Sambuc 	return 0;
182433d6423SLionel Sambuc 
183433d6423SLionel Sambuc }
184433d6423SLionel Sambuc 
185433d6423SLionel Sambuc /****************************************************************************
186433d6423SLionel Sambuc  *     devman_init                                                          *
187433d6423SLionel Sambuc  ***************************************************************************/
devman_init(void)188433d6423SLionel Sambuc int devman_init(void)
189433d6423SLionel Sambuc {
190433d6423SLionel Sambuc 	int res;
191433d6423SLionel Sambuc 
192433d6423SLionel Sambuc 	/* get the endpoint of the HCD */
193433d6423SLionel Sambuc 	res = ds_retrieve_label_endpt("devman", &devman_ep);
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc 	if (res != 0) {
196433d6423SLionel Sambuc 		panic("usb_init: ds_retrieve_label_endpt failed for 'devman': %d", res);
197433d6423SLionel Sambuc 	}
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc 	TAILQ_INIT(&dev_list);
200433d6423SLionel Sambuc 
201433d6423SLionel Sambuc 	return res;
202433d6423SLionel Sambuc }
203433d6423SLionel Sambuc 
204433d6423SLionel Sambuc /****************************************************************************
205433d6423SLionel Sambuc  *     do_bind                                                              *
206433d6423SLionel Sambuc  ***************************************************************************/
do_bind(message * m)207433d6423SLionel Sambuc static void do_bind(message *m)
208433d6423SLionel Sambuc {
209433d6423SLionel Sambuc 	struct devman_dev *dev;
210433d6423SLionel Sambuc 	int res;
211433d6423SLionel Sambuc 
212433d6423SLionel Sambuc 	/* find device */
213433d6423SLionel Sambuc 	TAILQ_FOREACH(dev, &dev_list, dev_list) {
214433d6423SLionel Sambuc 		if (dev->dev_id == m->DEVMAN_DEVICE_ID) {
215433d6423SLionel Sambuc 			if (dev->bind_cb) {
216433d6423SLionel Sambuc 				res = dev->bind_cb(dev->data, m->DEVMAN_ENDPOINT);
217433d6423SLionel Sambuc 				m->m_type = DEVMAN_REPLY;
218433d6423SLionel Sambuc 				m->DEVMAN_RESULT = res;
219433d6423SLionel Sambuc 				ipc_send(devman_ep, m);
220433d6423SLionel Sambuc 				return;
221433d6423SLionel Sambuc 			}
222433d6423SLionel Sambuc 		}
223433d6423SLionel Sambuc 	}
224433d6423SLionel Sambuc 	m->m_type = DEVMAN_REPLY;
225433d6423SLionel Sambuc 	m->DEVMAN_RESULT = ENODEV;
226433d6423SLionel Sambuc 	ipc_send(devman_ep, m);
227433d6423SLionel Sambuc 	return;
228433d6423SLionel Sambuc }
229433d6423SLionel Sambuc 
230433d6423SLionel Sambuc /****************************************************************************
231433d6423SLionel Sambuc  *     do_unbind                                                            *
232433d6423SLionel Sambuc  ***************************************************************************/
do_unbind(message * m)233433d6423SLionel Sambuc static void do_unbind(message *m)
234433d6423SLionel Sambuc {
235433d6423SLionel Sambuc 	struct devman_dev *dev;
236433d6423SLionel Sambuc 	int res;
237433d6423SLionel Sambuc 
238433d6423SLionel Sambuc 	/* find device */
239433d6423SLionel Sambuc 	TAILQ_FOREACH(dev, &dev_list, dev_list) {
240433d6423SLionel Sambuc 		if (dev->dev_id == m->DEVMAN_DEVICE_ID) {
241433d6423SLionel Sambuc 			if (dev->unbind_cb) {
242433d6423SLionel Sambuc 				res = dev->unbind_cb(dev->data, m->DEVMAN_ENDPOINT);
243433d6423SLionel Sambuc 				m->m_type = DEVMAN_REPLY;
244433d6423SLionel Sambuc 				m->DEVMAN_RESULT = res;
245433d6423SLionel Sambuc 				ipc_send(devman_ep, m);
246433d6423SLionel Sambuc 				return;
247433d6423SLionel Sambuc 			}
248433d6423SLionel Sambuc 		}
249433d6423SLionel Sambuc 	}
250433d6423SLionel Sambuc 	m->m_type = DEVMAN_REPLY;
251433d6423SLionel Sambuc 	m->DEVMAN_RESULT = ENODEV;
252433d6423SLionel Sambuc 	ipc_send(devman_ep, m);
253433d6423SLionel Sambuc }
254433d6423SLionel Sambuc 
255433d6423SLionel Sambuc /****************************************************************************
256433d6423SLionel Sambuc  *     devman_handle_msg                                                    *
257433d6423SLionel Sambuc  ***************************************************************************/
devman_handle_msg(message * m)258433d6423SLionel Sambuc int devman_handle_msg(message *m)
259433d6423SLionel Sambuc {
260433d6423SLionel Sambuc 	/* make sure msg comes from devman server */
261433d6423SLionel Sambuc 	if (m->m_source != devman_ep) {
262433d6423SLionel Sambuc 		/* we don't honor requests from others by answering them */
263433d6423SLionel Sambuc 		return 0;
264433d6423SLionel Sambuc 	}
265433d6423SLionel Sambuc 	switch (m->m_type) {
266433d6423SLionel Sambuc 		case DEVMAN_BIND:
267433d6423SLionel Sambuc 			do_bind(m);
268433d6423SLionel Sambuc 			return 1;
269433d6423SLionel Sambuc 		case DEVMAN_UNBIND:
270433d6423SLionel Sambuc 			do_unbind(m);
271433d6423SLionel Sambuc 			return 1;
272433d6423SLionel Sambuc 		default:
273433d6423SLionel Sambuc 			return 0;
274433d6423SLionel Sambuc 	}
275433d6423SLionel Sambuc }
276