1/*
2    Activate the smartcard interface on the kobil midentity usb device
3    Copyright (C) 2006  Norbert Federa <norbert.federa@neoware.com>
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15	You should have received a copy of the GNU Lesser General Public License
16	along with this library; if not, write to the Free Software Foundation,
17	Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
18
19Author:	Norbert Federa <norbert.federa@neoware.com>
20Date:   2006-04-06
21
22
23Description:
24
25This tool is needed to activate the smartcard interface on the kobil midentity
26usb device (vendor 0x04D6 id 0x4081)
27
28Kobil's own implementation was a kernel usb driver which did just send a
29libusb_control_transfer in the probe routine.
30
31We do the same via libusb and call this program from our /sbin/hotblug script
32if the mIDentity gets added.
33
34The kobil switcher driver was found inside this zip ...
35http://www.kobil.com/download/partner/KOBIL_mIDentity_SDK_Build_20060320_RELEASE.zip
36... under Interfaces/Linux/module_with_binary_final.tar.gz.
37
38Here the interesting part of the kernel driver inside the probe function:
39
40        if (dev->descriptor.idVendor == KOBIL_VENDOR_ID){
41                printk("!!!!! DEVICE FOUND !!! !\n");
42		ret = libusb_control_transfer(dev,
43		send_pipe,
44		0x09,
45		0x22,
46		0x0200,
47		0x0001,
48		switchCmd,
49		sizeof(switchCmd),
50		5000);
51	}
52
53Initally the it did not work with libusb because the ioctl gets ignored with
54the used RequestType of 0x22 in combination with index 0x0001, but index 0x0002
55worked.  See usb/devio.c functions  proc_control() ->  check_ctrlrecip() ->
56findintfep() in order to understand why.
57*/
58
59#include <stdio.h>
60#include <string.h>
61#include <unistd.h>
62#include <stdlib.h>
63#include <libusb.h>
64#include <errno.h>
65
66#include "config.h"
67
68#define KOBIL_VENDOR_ID		0x0D46
69#define MID_DEVICE_ID		0x4081
70#define KOBIL_TIMEOUT		5000
71#define VAL_STARTUP_4080        1
72#define VAL_STARTUP_4000        2
73#define VAL_STARTUP_4020        3
74#define VAL_STARTUP_40A0        4
75#define HIDCMD_SWITCH_DEVICE    0x0004
76
77#define bmRequestType           0x22
78#define bRequest                0x09
79#define wValue                  0x0200
80#define wIndex                  0x0002  /* this was originally 0x0001 */
81
82
83static int kobil_midentity_control_msg(libusb_device_handle *usb)
84{
85	int ret;
86
87	unsigned char switchCmd[10];
88
89	unsigned char Sleep = 1;
90	unsigned char hardDisk = 1;
91
92	unsigned char param = ((hardDisk) << 4) | (Sleep);
93
94	memset(switchCmd, 0x0, sizeof(switchCmd));
95	switchCmd[0] = HIDCMD_SWITCH_DEVICE >> 8;
96	switchCmd[1] = HIDCMD_SWITCH_DEVICE;
97	switchCmd[5] = VAL_STARTUP_4000;
98	switchCmd[9] = param;
99
100	ret = libusb_control_transfer(usb, bmRequestType, bRequest, wValue, wIndex,
101			switchCmd, sizeof(switchCmd), KOBIL_TIMEOUT);
102
103	return(!(ret==sizeof(switchCmd)));
104}
105
106
107static int kobil_midentity_claim_interface(libusb_device_handle *usb, int ifnum)
108{
109	int rv;
110
111	printf("claiming interface #%d ... ", ifnum);
112	rv = libusb_claim_interface(usb, ifnum);
113	if (rv == 0)
114	{
115		printf("success\n");
116		return rv;
117	}
118	else
119		printf("failed\n");
120
121	printf("failed with error %d, trying to detach kernel driver ....\n", rv);
122	rv = libusb_detach_kernel_driver(usb, ifnum);
123	if (rv == 0)
124	{
125		printf("success, claiming interface again ...");
126		rv = libusb_claim_interface(usb, ifnum);
127		if (rv == 0)
128		{
129			printf("success\n");
130			return rv;
131		}
132		else
133			printf("failed\n");
134	}
135
136	printf("failed with error %d, giving up.\n", rv);
137	return rv;
138}
139
140
141int main(int argc, char *argv[])
142{
143	libusb_device **devs, *dev;
144	libusb_device *found_dev = NULL;
145	struct libusb_device_handle *usb = NULL;
146	int rv, i;
147	ssize_t cnt;
148
149	(void)argc;
150	(void)argv;
151
152	rv = libusb_init(NULL);
153	if (rv < 0)
154	{
155		(void)printf("libusb_init() failed\n");
156		return rv;
157	}
158
159	cnt = libusb_get_device_list(NULL, &devs);
160	if (cnt < 0)
161	{
162		(void)printf("libusb_get_device_list() failed\n");
163		return (int)cnt;
164	}
165
166	/* for every device */
167	i = 0;
168	while ((dev = devs[i++]) != NULL)
169	{
170		struct libusb_device_descriptor desc;
171
172		rv = libusb_get_device_descriptor(dev, &desc);
173		if (rv < 0) {
174			(void)printf("failed to get device descriptor\n");
175			continue;
176		}
177
178		printf("vendor/product: %04X %04X\n", desc.idVendor, desc.idProduct);
179		if (desc.idVendor == KOBIL_VENDOR_ID && desc.idProduct == MID_DEVICE_ID)
180			found_dev = dev;
181	}
182
183	if (found_dev == NULL)
184	{
185		printf("device not found. aborting.\n");
186		if (0 != geteuid())
187			printf("Try to rerun this program as root.\n");
188		exit(1);
189	}
190
191	printf("Device found, opening ... ");
192	rv = libusb_open(found_dev, &usb);
193	if (rv < 0)
194	{
195		printf("failed, aborting.\n");
196		exit(2);
197	}
198	printf("success\n");
199
200	rv = kobil_midentity_claim_interface(usb, 0);
201	if (rv < 0)
202	{
203		libusb_close(usb);
204		exit(3);
205	}
206
207	rv = kobil_midentity_claim_interface(usb, 1);
208	if (rv < 0)
209	{
210		libusb_close(usb);
211		exit(3);
212	}
213
214	printf("Activating the CCID configuration .... ");
215	rv = kobil_midentity_control_msg(usb);
216	if (rv == 0)
217		printf("success\n");
218	else
219		printf("failed with error %d, giving up.\n", rv);
220
221	libusb_close(usb);
222
223	return 0;
224}
225
226