1/* @file plustek-pp_procfs.c
2 * @brief this is the interface to the proc filesystem
3 *
4 * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de>
5 *
6 * History:
7 * - 0.37 - initial version
8 * - 0.38 - changes according to generic structure changes
9 * - 0.39 - added info about forceMode and slowIO
10 * - 0.40 - no changes
11 * - 0.41 - no changes
12 * - 0.42 - changed include names
13 * - 0.43 - replace _PTDRV_VERx by _PTDRV_VERSTR
14 *        - cleanup
15 * - 0.44 - PROC_FS changes for newer kernel
16 *        - fix format string issues, as Long types default to int32_t
17 *          now
18 * .
19 * <hr>
20 * This file is part of the SANE package.
21 *
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License as
24 * published by the Free Software Foundation; either version 2 of the
25 * License, or (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful, but
28 * WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
30 * General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
34 *
35 * As a special exception, the authors of SANE give permission for
36 * additional uses of the libraries contained in this release of SANE.
37 *
38 * The exception is that, if you link a SANE library with other files
39 * to produce an executable, this does not by itself cause the
40 * resulting executable to be covered by the GNU General Public
41 * License.  Your use of that executable is in no way restricted on
42 * account of linking the SANE library code into it.
43 *
44 * This exception does not, however, invalidate any other reasons why
45 * the executable file might be covered by the GNU General Public
46 * License.
47 *
48 * If you submit changes to SANE to the maintainers to be included in
49 * a subsequent release, you agree by submitting the changes that
50 * those changes may be distributed with this exception intact.
51 *
52 * If you write modifications of your own for SANE, it is your choice
53 * whether to permit this exception to apply to your modifications.
54 * If you do not wish that, delete this exception notice.
55 * <hr>
56 */
57#ifdef __KERNEL__
58#include <linux/proc_fs.h>
59
60#include "plustek-pp_scan.h"
61
62/* toggled by your kernel configuration */
63#ifdef CONFIG_PROC_FS
64
65/****************************** static vars **********************************/
66
67/** for the proc filesystem
68 */
69#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
70extern struct proc_dir_entry proc_root;
71#endif
72static struct proc_dir_entry *base  = NULL;
73static struct proc_dir_entry *binfo = NULL;
74static ULong                  devcount;
75
76/** parallel port modes... */
77static char *procfsPortModes[] = {
78	"EPP",
79	"SPP",
80	"BiDi (PS/2)",
81	"ECP"
82	"unknown",
83	NULL
84};
85
86/** CCD-Types (as for ASIC 98001 based series) */
87static TabDef procfsCCDTypes98001[] = {
88
89	{ _CCD_3797, "3797" },
90	{ _CCD_3717, "3717" },
91	{ _CCD_535,  "535"  },
92	{ _CCD_2556, "2556" },
93	{ _CCD_518,  "518"  },
94	{ _CCD_539,  "539"  },
95    { -1 ,       "unknown" }
96};
97
98/** CCD-Types (as for ASIC 98003 based series) */
99static TabDef procfsCCDTypes98003[] = {
100
101	{ _CCD_3797, "3797" },
102	{ _CCD_3799, "3799" },
103	{ _CCD_535,  "535"  },
104	{ _CCD_2556, "2556" },
105	{ _CCD_518,  "518"  },
106	{ _CCD_539,  "539"  },
107    { _CCD_3777, "3777"	},
108    { _CCD_548 , "548"	},
109    { -1 ,       "unknown" }
110};
111
112/****************************** local functions ******************************/
113
114#ifndef LINUX_24
115/** This is called as the fill_inode function when an inode
116 * is going into (fill = 1) or out of service (fill = 0).
117 *
118 * Note: only the top-level directory needs to do this; if
119 * a lower level is referenced, the parent will be as well.
120 *
121 * Here simply a dummy function
122 */
123static void procfsFillFunc( struct inode *inode, int fill )
124{
125}
126#endif
127
128/** returns a pointer to the port-mode string
129 */
130static const char* procfsGetMode( int mode )
131{
132	if((mode < _PORT_EPP) || (mode > _PORT_ECP))
133		return procfsPortModes[_PORT_ECP+1];
134
135	return procfsPortModes[mode];
136}
137
138/** determines CCD-Type string
139 */
140static const char* procfsGetCCDType( pScanData ps )
141{
142	int     i;
143	int     ccd_id = ps->Device.bCCDID;
144	pTabDef tab = procfsCCDTypes98001;
145
146	if( _IS_ASIC98(ps->sCaps.AsicID)) {
147
148        if(_ASIC_IS_98003 == ps->sCaps.AsicID)
149            tab = procfsCCDTypes98003;
150
151        /* seek down the description table */
152        for( i = 0; -1 != tab[i].id; i++ ) {
153
154            if( tab[i].id == ccd_id )
155                return tab[i].desc;
156        }
157    } else {
158
159        /* for older scanners only this info is available */
160        if( ps->fSonyCCD )
161            return "SONY Type";
162        else
163            return "NEC/TOSHIBA Type";
164    }
165
166	/* return the last entry if nothing applies! */
167	return tab[(sizeof(procfsCCDTypes98001)/sizeof(TabDef)-1)].desc;
168}
169
170/** will be called when reading the proc filesystem:
171 * cat /proc/pt_drv/info
172 */
173static int procfsBInfoReadProc( char *buf, char **start, off_t offset,
174                                int count, int *eof, void *data )
175{
176	int len = 0;
177
178	len += sprintf( buf, "Plustek Flatbed Scanner Driver version "_PTDRV_VERSTR"\n" );
179	len += sprintf( buf + len, "IOCTL-Version: 0x%08x\n",_PTDRV_IOCTL_VERSION);
180	return len;
181}
182
183/** will be called when reading the proc filesystem:
184 * cat /proc/pt_drv/deviceX/info
185 */
186static int procfsInfoReadProc( char *buf, char **start, off_t offset,
187                               int count, int *eof, void *data )
188{
189	int       len = 0;
190	pScanData ps  = (pScanData)data;
191
192	/* Tell us something about the device... */
193	if( NULL != ps ) {
194		len += sprintf( buf+len, "Model       : %s\n",
195					    MiscGetModelName(ps->sCaps.Model));
196		len += sprintf( buf+len, "Portaddress : 0x%X\n", ps->IO.portBase );
197		len += sprintf( buf+len, "Portmode    : %s (%s I/O, %s)\n",
198					    procfsGetMode(ps->IO.portMode),
199						(ps->IO.slowIO == _TRUE?"delayed":"fast"),
200                        (ps->IO.forceMode == 0?"autodetect":"forced"));
201		len += sprintf( buf+len, "Buttons     : %u\n",  ps->Device.buttons);
202		len += sprintf( buf+len, "Warmuptime  : %us\n", ps->warmup        );
203		len += sprintf( buf+len, "Lamp timeout: %us\n", ps->lampoff       );
204		len += sprintf( buf+len, "mov-switch  : %u\n",  ps->ModelOverride );
205		len += sprintf( buf+len, "I/O-delay   : %u\n",  ps->IO.delay      );
206		len += sprintf( buf+len, "CCD-Type    : %s\n",  procfsGetCCDType(ps));
207        len += sprintf( buf+len, "TPA         : %s\n",
208                        (ps->sCaps.dwFlag & SFLAG_TPA) ? "yes":"no" );
209	}
210
211	return len;
212}
213
214/** will be called when reading the proc filesystem:
215 * cat /proc/pt_drv/devicex/buttony
216 */
217static int procfsButtonsReadProc( char *buf, char **start, off_t offset,
218                                  int count, int *eof, void *data )
219{
220	Byte	  b;
221	int       bc  = 0;
222	int       len = 0;
223	pScanData ps  = (pScanData)data;
224
225	if( NULL != ps ) {
226		bc = ps->Device.buttons;
227	}
228
229	/* Check the buttons... */
230	if( 0 != bc ) {
231
232		if ( _ASIC_IS_96003 == ps->sCaps.AsicID ) {
233			MiscClaimPort( ps );
234			b = IODataRegisterFromScanner( ps, ps->RegStatus );
235			if(_FLAG_P96_KEY == (b & _FLAG_P96_KEY))
236				b = 0;
237			else
238				b = 1;
239			MiscReleasePort( ps );
240			len += sprintf( buf + len, "%u\n", b );
241		} else
242			bc = 0;
243	}
244
245	if( 0 == bc )
246		len += sprintf( buf + len, "none\n" );
247
248	return len;
249}
250
251/** create a procfs entry
252 */
253static struct proc_dir_entry *new_entry( const char *name, mode_t mode,
254                                         struct proc_dir_entry *parent )
255{
256#ifndef LINUX_24
257	int len;
258#endif
259	struct proc_dir_entry *ent;
260
261	if (mode == S_IFDIR)
262		mode |= S_IRUGO | S_IXUGO;
263	else if (mode == 0)
264		mode = S_IFREG | S_IRUGO;
265
266#ifndef LINUX_24
267	len = strlen(name) + 1;
268
269	/* allocate memory for the entry and the name */
270	ent = kmalloc(sizeof(struct proc_dir_entry) + len, GFP_KERNEL);
271	if( NULL == ent )
272		return NULL;
273
274	memset(ent, 0, sizeof(struct proc_dir_entry));
275
276	/* position pointer of name to end of the structure*/
277	ent->name = ((char *) ent) + sizeof(*ent);
278	strcpy((char *)ent->name, name );
279
280	ent->namelen = strlen(name);
281	ent->mode    = mode;
282
283	if (S_ISDIR(mode)) {
284		ent->nlink      = 2;
285		ent->fill_inode = &procfsFillFunc;
286	} else {
287		ent->nlink = 1;
288	}
289
290	proc_register( parent, ent );
291#else
292	if (mode == S_IFDIR)
293		ent = proc_mkdir( name, parent );
294	else
295		ent = create_proc_entry( name, mode, parent );
296#endif
297
298	return ent;
299}
300
301/** shutdown one proc fs entry
302 */
303static inline void destroy_proc_entry( struct proc_dir_entry *root,
304                                       struct proc_dir_entry **d )
305{
306#ifndef LINUX_24
307	proc_unregister( root, (*d)->low_ino );
308	kfree(*d);
309#else
310	DBG(DBG_HIGH, "pt_drv: proc del '%s' root='%s'\n", (*d)->name, root->name);
311
312	remove_proc_entry((*d)->name, root );
313#endif
314
315	*d = NULL;
316}
317
318/** shutdown the proc-tree for one device
319 */
320static void destroy_proc_tree( pScanData ps )
321{
322	int i;
323
324	DBG( DBG_HIGH, "pt_drv: destroy_proc_tree !\n" );
325
326	if( ps ) {
327
328		if( ps->procDir.entry ) {
329
330			if( ps->procDir.info )
331				destroy_proc_entry( ps->procDir.entry, &ps->procDir.info );
332
333			for( i = 0; i < ps->Device.buttons; i++ ) {
334
335				if( ps->procDir.buttons[i] )
336    				destroy_proc_entry(ps->procDir.entry, &ps->procDir.buttons[i]);
337			}
338
339			destroy_proc_entry( base, &ps->procDir.entry );
340		}
341	}
342}
343
344/*************************** exported functions ******************************/
345
346/** initialize our proc-fs stuff
347 */
348int ProcFsInitialize( void )
349{
350	DBG( DBG_HIGH, "ProcFsInitialize()\n" );
351
352#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
353	base = new_entry( _DRV_NAME, S_IFDIR, &proc_root );
354#else
355	base = new_entry( _DRV_NAME, S_IFDIR, NULL );
356#endif
357
358	if( NULL != base ) {
359
360		devcount = 0;
361
362		binfo = new_entry( "info", 0, base );
363		if( NULL != binfo ) {
364			binfo->read_proc = procfsBInfoReadProc;
365			binfo->data      = &devcount;
366		}
367	}
368
369	return _OK;
370}
371
372/** cleanup the base entry
373 */
374void ProcFsShutdown( void )
375{
376	DBG( DBG_HIGH, "ProcFsShutdown()\n" );
377
378	if( NULL != base ) {
379
380		if( NULL != binfo )
381			destroy_proc_entry( base, &binfo );
382
383#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
384		destroy_proc_entry( &proc_root, &base );
385#else
386		destroy_proc_entry( NULL, &base );
387#endif
388	}
389
390	devcount = 0;
391}
392
393/** will be called for each device, that has been found
394 */
395void ProcFsRegisterDevice( pScanData ps )
396{
397	int	 i;
398	char str[20];
399
400	if( NULL == base ) {
401	    printk( KERN_ERR "pt_drv : proc not initialised yet!\n");
402		return;
403	}
404
405	memset( &ps->procDir, 0, sizeof(ProcDirDef));
406
407	sprintf( str, "device%u", ps->devno );
408
409	ps->procDir.entry = new_entry( str, S_IFDIR, base );
410	if( NULL == ps->procDir.entry )
411		goto error_exit;
412
413	ps->procDir.info = new_entry( "info", 0, ps->procDir.entry );
414	if( NULL == ps->procDir.info )
415		goto error_exit;
416
417	ps->procDir.info->read_proc = procfsInfoReadProc;
418	ps->procDir.info->data      = ps;
419
420	for( i = 0; i < ps->Device.buttons; i++ ) {
421
422		sprintf( str, "button%u", i );
423
424		ps->procDir.buttons[i] = new_entry( str, 0, ps->procDir.entry );
425		if( NULL == ps->procDir.buttons[i] )
426			goto error_exit;
427
428		ps->procDir.buttons[i]->read_proc = procfsButtonsReadProc;
429		ps->procDir.buttons[i]->data      = ps;
430	}
431
432	devcount++;
433	return;
434
435
436error_exit:
437
438	printk(KERN_ERR "pt_drv: failure registering /proc/ entry %s.\n", str );
439	destroy_proc_tree( ps );
440}
441
442/** cleanup the proc-fs for a certain device
443 */
444void ProcFsUnregisterDevice( pScanData ps )
445{
446	destroy_proc_tree( ps );
447}
448
449#else 	/* CONFIG_PROC_FS */
450
451int  ProcFsInitialize( void )
452{
453	return _OK;
454}
455
456void ProcFsShutdown( void )
457{
458}
459
460void ProcFsRegisterDevice( pScanData ps )
461{
462}
463
464void ProcFsUnregisterDevice( pScanData ps )
465{
466}
467
468#endif
469
470#endif /* guard __KERNEL__ */
471
472/* END PLUSTEK-PP_PROCFS.C ..................................................*/
473