1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * hci1394_ioctl.c
29  *   Test ioctl's to support test/debug of the 1394 HW. hci1394_ioctl_enum_t is
30  *   passed in cmd and a pointer to the appropriate structure (i.e.
31  *   hci1394_ioctl_wrreg_t) is passed in arg.
32  */
33 
34 #include <sys/conf.h>
35 #include <sys/modctl.h>
36 #include <sys/mkdev.h>
37 #include <sys/cred.h>
38 #include <sys/file.h>
39 #include <sys/types.h>
40 #include <sys/errno.h>
41 #include <sys/ddi.h>
42 #include <sys/sunddi.h>
43 
44 #include <sys/1394/h1394.h>
45 #include <sys/1394/adapters/hci1394.h>
46 #include <sys/1394/adapters/hci1394_extern.h>
47 #include <sys/1394/adapters/hci1394_ioctl.h>
48 
49 
50 /* HCI1394_IOCTL_READ_SELFID for 32-bit apps in 64-bit kernel */
51 typedef struct hci1394_ioctl_readselfid32_s {
52 	uint32_t buf;
53 	uint_t count;
54 } hci1394_ioctl_readselfid32_t;
55 
56 
57 static int hci1394_ioctl_wrreg(hci1394_state_t *soft_state, void *arg,
58     int mode);
59 static int hci1394_ioctl_rdreg(hci1394_state_t *soft_state, void *arg,
60     int mode);
61 static int hci1394_ioctl_wrvreg(hci1394_state_t *soft_state, void *arg,
62     int mode);
63 static int hci1394_ioctl_rdvreg(hci1394_state_t *soft_state, void *arg,
64     int mode);
65 static int hci1394_ioctl_selfid_cnt(hci1394_state_t *soft_state, void *arg,
66     int mode);
67 static int hci1394_ioctl_busgen_cnt(hci1394_state_t *soft_state, void *arg,
68     int mode);
69 static int hci1394_ioctl_wrphy(hci1394_state_t *soft_state, void *arg,
70     int mode);
71 static int hci1394_ioctl_rdphy(hci1394_state_t *soft_state, void *arg,
72     int mode);
73 static int hci1394_ioctl_hbainfo(hci1394_state_t *soft_state, void *arg,
74     int mode);
75 static int hci1394_ioctl_read_selfid(hci1394_state_t *soft_state, void *arg,
76     int mode);
77 #ifdef	_MULTI_DATAMODEL
78 static int hci1394_ioctl_read_selfid32(hci1394_state_t *soft_state,
79     hci1394_ioctl_readselfid32_t *read_selfid, int mode);
80 #endif
81 
82 
83 /* ARGSUSED */
84 int
hci1394_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)85 hci1394_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
86     int *rvalp)
87 {
88 	hci1394_state_t *soft_state;
89 	int instance;
90 	int status;
91 
92 	instance = getminor(dev);
93 	if (instance == -1) {
94 		return (EBADF);
95 	}
96 
97 	soft_state = ddi_get_soft_state(hci1394_statep, instance);
98 	if (soft_state == NULL) {
99 		return (EBADF);
100 	}
101 
102 	status = 0;
103 
104 	switch (cmd) {
105 	case HCI1394_IOCTL_WRITE_REG:
106 		status = hci1394_ioctl_wrreg(soft_state, (void *)arg, mode);
107 		break;
108 	case HCI1394_IOCTL_READ_REG:
109 		status = hci1394_ioctl_rdreg(soft_state, (void *)arg, mode);
110 		break;
111 	case HCI1394_IOCTL_READ_VREG:
112 		status = hci1394_ioctl_rdvreg(soft_state, (void *)arg, mode);
113 		break;
114 	case HCI1394_IOCTL_WRITE_VREG:
115 		status = hci1394_ioctl_wrvreg(soft_state, (void *)arg, mode);
116 		break;
117 	case HCI1394_IOCTL_RESET_BUS:
118 		status = hci1394_ohci_bus_reset(soft_state->ohci);
119 		break;
120 	case HCI1394_IOCTL_SELFID_CNT:
121 		status = hci1394_ioctl_selfid_cnt(soft_state, (void *)arg,
122 		    mode);
123 		break;
124 	case HCI1394_IOCTL_BUSGEN_CNT:
125 		status = hci1394_ioctl_busgen_cnt(soft_state, (void *)arg,
126 		    mode);
127 		break;
128 	case HCI1394_IOCTL_READ_SELFID:
129 		status = hci1394_ioctl_read_selfid(soft_state, (void *)arg,
130 		    mode);
131 		break;
132 	case HCI1394_IOCTL_READ_PHY:
133 		status = hci1394_ioctl_rdphy(soft_state, (void *)arg, mode);
134 		break;
135 	case HCI1394_IOCTL_WRITE_PHY:
136 		status = hci1394_ioctl_wrphy(soft_state, (void *)arg, mode);
137 		break;
138 	case HCI1394_IOCTL_HBA_INFO:
139 		status = hci1394_ioctl_hbainfo(soft_state, (void *)arg, mode);
140 		break;
141 	default:
142 		/*
143 		 * if we don't know what the ioctl is, forward it on to the
144 		 * services layer.  The services layer will handle the devctl
145 		 * ioctl's along with any services layer private ioctls that
146 		 * it has defined.
147 		 */
148 		status = h1394_ioctl(soft_state->drvinfo.di_sl_private, cmd,
149 		    arg, mode, credp, rvalp);
150 		break;
151 	}
152 
153 	return (status);
154 }
155 
156 
157 static int
hci1394_ioctl_wrreg(hci1394_state_t * soft_state,void * arg,int mode)158 hci1394_ioctl_wrreg(hci1394_state_t *soft_state, void *arg, int mode)
159 {
160 	hci1394_ioctl_wrreg_t wrreg;
161 	int status;
162 
163 
164 	ASSERT(soft_state != NULL);
165 	ASSERT(arg != NULL);
166 
167 	status = ddi_copyin(arg, &wrreg, sizeof (hci1394_ioctl_wrreg_t), mode);
168 	if (status != 0) {
169 		return (EFAULT);
170 	}
171 
172 	hci1394_ohci_reg_write(soft_state->ohci, wrreg.addr, wrreg.data);
173 
174 	return (0);
175 }
176 
177 
178 static int
hci1394_ioctl_rdreg(hci1394_state_t * soft_state,void * arg,int mode)179 hci1394_ioctl_rdreg(hci1394_state_t *soft_state, void *arg, int mode)
180 {
181 	hci1394_ioctl_rdreg_t rdreg;
182 	int status;
183 
184 
185 	ASSERT(soft_state != NULL);
186 	ASSERT(arg != NULL);
187 
188 	status = ddi_copyin(arg, &rdreg, sizeof (hci1394_ioctl_rdreg_t), mode);
189 	if (status != 0) {
190 		return (EFAULT);
191 	}
192 
193 	hci1394_ohci_reg_read(soft_state->ohci, rdreg.addr, &rdreg.data);
194 
195 	status = ddi_copyout(&rdreg, arg, sizeof (hci1394_ioctl_rdreg_t), mode);
196 	if (status != 0) {
197 		return (EFAULT);
198 	}
199 
200 	return (0);
201 }
202 
203 
204 static int
hci1394_ioctl_wrvreg(hci1394_state_t * soft_state,void * arg,int mode)205 hci1394_ioctl_wrvreg(hci1394_state_t *soft_state, void *arg, int mode)
206 {
207 	hci1394_ioctl_wrvreg_t wrvreg;
208 	int status;
209 
210 
211 	ASSERT(soft_state != NULL);
212 	ASSERT(arg != NULL);
213 
214 	status = ddi_copyin(arg, &wrvreg, sizeof (hci1394_ioctl_wrvreg_t),
215 	    mode);
216 	if (status != 0) {
217 		return (EFAULT);
218 	}
219 
220 	status = hci1394_vendor_reg_write(soft_state->vendor,
221 	    wrvreg.regset, wrvreg.addr, wrvreg.data);
222 	if (status != DDI_SUCCESS) {
223 		return (EINVAL);
224 	}
225 
226 	return (0);
227 }
228 
229 
230 static int
hci1394_ioctl_rdvreg(hci1394_state_t * soft_state,void * arg,int mode)231 hci1394_ioctl_rdvreg(hci1394_state_t *soft_state, void *arg, int mode)
232 {
233 	hci1394_ioctl_rdvreg_t rdvreg;
234 	int status;
235 
236 
237 	ASSERT(soft_state != NULL);
238 	ASSERT(arg != NULL);
239 
240 	status = ddi_copyin(arg, &rdvreg, sizeof (hci1394_ioctl_rdvreg_t),
241 	    mode);
242 	if (status != 0) {
243 		return (EFAULT);
244 	}
245 
246 	status = hci1394_vendor_reg_read(soft_state->vendor,
247 	    rdvreg.regset, rdvreg.addr, &rdvreg.data);
248 	if (status != DDI_SUCCESS) {
249 		return (EINVAL);
250 	}
251 
252 	status = ddi_copyout(&rdvreg, arg, sizeof (hci1394_ioctl_rdvreg_t),
253 	    mode);
254 	if (status != 0) {
255 		return (EFAULT);
256 	}
257 
258 	return (0);
259 }
260 
261 
262 static int
hci1394_ioctl_selfid_cnt(hci1394_state_t * soft_state,void * arg,int mode)263 hci1394_ioctl_selfid_cnt(hci1394_state_t *soft_state, void *arg, int mode)
264 {
265 	hci1394_ioctl_selfid_cnt_t selfid_cnt;
266 	int status;
267 
268 
269 	ASSERT(soft_state != NULL);
270 	ASSERT(arg != NULL);
271 
272 	selfid_cnt.count = soft_state->drvinfo.di_stats.st_selfid_count;
273 
274 	status = ddi_copyout(&selfid_cnt, arg,
275 	    sizeof (hci1394_ioctl_selfid_cnt_t), mode);
276 	if (status != 0) {
277 		return (EFAULT);
278 	}
279 
280 	return (0);
281 }
282 
283 
284 static int
hci1394_ioctl_busgen_cnt(hci1394_state_t * soft_state,void * arg,int mode)285 hci1394_ioctl_busgen_cnt(hci1394_state_t *soft_state, void *arg, int mode)
286 {
287 	hci1394_ioctl_busgen_cnt_t busgen_cnt;
288 	int status;
289 
290 
291 	ASSERT(soft_state != NULL);
292 	ASSERT(arg != NULL);
293 
294 	busgen_cnt.count = hci1394_ohci_current_busgen(soft_state->ohci);
295 
296 	status = ddi_copyout(&busgen_cnt, arg,
297 	    sizeof (hci1394_ioctl_busgen_cnt_t), mode);
298 	if (status != 0) {
299 		return (EFAULT);
300 	}
301 
302 	return (0);
303 }
304 
305 
306 static int
hci1394_ioctl_wrphy(hci1394_state_t * soft_state,void * arg,int mode)307 hci1394_ioctl_wrphy(hci1394_state_t *soft_state, void *arg, int mode)
308 {
309 	hci1394_ioctl_wrphy_t wrphy;
310 	int status;
311 
312 
313 	ASSERT(soft_state != NULL);
314 	ASSERT(arg != NULL);
315 
316 	status = ddi_copyin(arg, &wrphy, sizeof (hci1394_ioctl_wrphy_t), mode);
317 	if (status != 0) {
318 		return (EFAULT);
319 	}
320 
321 	status = hci1394_ohci_phy_write(soft_state->ohci, wrphy.addr,
322 	    wrphy.data);
323 	if (status != DDI_SUCCESS) {
324 		return (EINVAL);
325 	}
326 
327 	return (0);
328 }
329 
330 
331 static int
hci1394_ioctl_rdphy(hci1394_state_t * soft_state,void * arg,int mode)332 hci1394_ioctl_rdphy(hci1394_state_t *soft_state, void *arg, int mode)
333 {
334 	hci1394_ioctl_rdphy_t rdphy;
335 	int status;
336 
337 
338 	ASSERT(soft_state != NULL);
339 	ASSERT(arg != NULL);
340 
341 	status = ddi_copyin(arg, &rdphy, sizeof (hci1394_ioctl_rdphy_t), mode);
342 	if (status != 0) {
343 		return (EFAULT);
344 	}
345 
346 	status = hci1394_ohci_phy_read(soft_state->ohci, rdphy.addr,
347 	    &rdphy.data);
348 	if (status != DDI_SUCCESS) {
349 		return (EINVAL);
350 	}
351 
352 	status = ddi_copyout(&rdphy, arg, sizeof (hci1394_ioctl_rdphy_t), mode);
353 	if (status != 0) {
354 		return (EFAULT);
355 	}
356 
357 	return (0);
358 }
359 
360 
361 static int
hci1394_ioctl_hbainfo(hci1394_state_t * soft_state,void * arg,int mode)362 hci1394_ioctl_hbainfo(hci1394_state_t *soft_state, void *arg, int mode)
363 {
364 	hci1394_ioctl_hbainfo_t hbainfo;
365 	int status;
366 
367 
368 	ASSERT(soft_state != NULL);
369 	ASSERT(arg != NULL);
370 
371 	hbainfo.pci_vendor_id = soft_state->vendor_info.vendor_id;
372 	hbainfo.pci_device_id = soft_state->vendor_info.device_id;
373 	hbainfo.pci_revision_id = soft_state->vendor_info.revision_id;
374 	hbainfo.ohci_version = soft_state->vendor_info.ohci_version;
375 	hbainfo.ohci_vendor_id = soft_state->vendor_info.ohci_vendor_id;
376 	hbainfo.ohci_vregset_cnt = soft_state->vendor_info.vendor_reg_count;
377 
378 	status = ddi_copyout(&hbainfo, arg, sizeof (hci1394_ioctl_hbainfo_t),
379 	    mode);
380 	if (status != 0) {
381 		return (EFAULT);
382 	}
383 
384 	return (0);
385 }
386 
387 
388 static int
hci1394_ioctl_read_selfid(hci1394_state_t * soft_state,void * arg,int mode)389 hci1394_ioctl_read_selfid(hci1394_state_t *soft_state, void *arg, int mode)
390 {
391 	hci1394_ioctl_read_selfid_t read_selfid;
392 	int status;
393 	uint_t offset;
394 	uint32_t data;
395 #ifdef	_MULTI_DATAMODEL
396 	hci1394_ioctl_readselfid32_t read_selfid32;
397 #endif
398 
399 
400 	ASSERT(soft_state != NULL);
401 	ASSERT(arg != NULL);
402 
403 #ifdef	_MULTI_DATAMODEL
404 	switch (ddi_model_convert_from(mode & FMODELS)) {
405 
406 		/* 32-bit app in 64-bit kernel */
407 	case DDI_MODEL_ILP32:
408 		/* copy in the 32-bit version of the args */
409 		status = ddi_copyin(arg, &read_selfid32,
410 		    sizeof (hci1394_ioctl_readselfid32_t), mode);
411 		if (status != 0) {
412 			return (EFAULT);
413 		}
414 
415 		/*
416 		 * Use a special function to process the 32-bit user address
417 		 * pointer embedded in the structure we pass in arg.
418 		 */
419 		status = hci1394_ioctl_read_selfid32(soft_state,
420 		    &read_selfid32, mode);
421 		return (status);
422 	default:
423 		break;
424 	}
425 #endif
426 
427 	/*
428 	 * if we got here, we either are a 64-bit app in a 64-bit kernel or a
429 	 * 32-bit app in a 32-bit kernel
430 	 */
431 
432 	/* copy in the args. We don't need to do any special conversions */
433 	status = ddi_copyin(arg, &read_selfid,
434 	    sizeof (hci1394_ioctl_read_selfid_t), mode);
435 	if (status != 0) {
436 		return (EFAULT);
437 	}
438 
439 	/*
440 	 * make sure we are not trying to copy more data than the selfid buffer
441 	 * can hold.  count is in quadlets and max_selfid_size is in bytes.
442 	 */
443 	if ((read_selfid.count * 4) > OHCI_MAX_SELFID_SIZE) {
444 		return (EINVAL);
445 	}
446 
447 	/*
448 	 * copy the selfid buffer one word at a time into the user buffer. The
449 	 * combination between having to do ddi_get32's (for endian reasons)
450 	 * and a ddi_copyout() make it easier to do it one word at a time.
451 	 */
452 	for (offset = 0; offset < read_selfid.count; offset++) {
453 		/* read word from selfid buffer */
454 		hci1394_ohci_selfid_read(soft_state->ohci, offset, &data);
455 
456 		/* copy the selfid word into the user buffer */
457 		status = ddi_copyout(&data, &read_selfid.buf[offset], 4, mode);
458 		if (status != 0) {
459 			return (EFAULT);
460 		}
461 	}
462 
463 	return (0);
464 }
465 
466 
467 #ifdef	_MULTI_DATAMODEL
468 static int
hci1394_ioctl_read_selfid32(hci1394_state_t * soft_state,hci1394_ioctl_readselfid32_t * read_selfid,int mode)469 hci1394_ioctl_read_selfid32(hci1394_state_t *soft_state,
470     hci1394_ioctl_readselfid32_t *read_selfid, int mode)
471 {
472 	int status;
473 	uint_t offset;
474 	uint32_t data;
475 
476 
477 	ASSERT(soft_state != NULL);
478 	ASSERT(read_selfid != NULL);
479 
480 	/*
481 	 * make sure we are not trying to copy more data than the selfid buffer
482 	 * can hold.  count is in quadlets and max_selfid_size is in bytes.
483 	 */
484 	if ((read_selfid->count * 4) > OHCI_MAX_SELFID_SIZE) {
485 		return (EINVAL);
486 	}
487 
488 	/*
489 	 * copy the selfid buffer one word at a time into the user buffer. The
490 	 * combination between having to do ddi_get32's (for endian reasons) and
491 	 * a ddi_copyout() make it easier to do it one word at a time.
492 	 */
493 	for (offset = 0; offset < read_selfid->count; offset++) {
494 		/* read word from selfid buffer */
495 		hci1394_ohci_selfid_read(soft_state->ohci, offset, &data);
496 		/* copy the selfid word into the user buffer */
497 		status = ddi_copyout(&data,
498 		    (void *)(uintptr_t)(read_selfid->buf + (offset * 4)),
499 		    4, mode);
500 		if (status != 0) {
501 			return (EFAULT);
502 		}
503 	}
504 
505 	return (0);
506 }
507 #endif
508