@@ -394,141 +394,161 @@ static int hidraw_revoke(struct hidraw_list *list)
394394 return 0 ;
395395}
396396
397- static long hidraw_ioctl (struct file * file , unsigned int cmd ,
398- unsigned long arg )
397+ static long hidraw_fixed_size_ioctl (struct file * file , struct hidraw * dev , unsigned int cmd ,
398+ void __user * arg )
399399{
400- struct inode * inode = file_inode (file );
401- unsigned int minor = iminor (inode );
402- long ret = 0 ;
403- struct hidraw * dev ;
404- struct hidraw_list * list = file -> private_data ;
405- void __user * user_arg = (void __user * ) arg ;
406-
407- down_read (& minors_rwsem );
408- dev = hidraw_table [minor ];
409- if (!dev || !dev -> exist || hidraw_is_revoked (list )) {
410- ret = - ENODEV ;
411- goto out ;
412- }
400+ struct hid_device * hid = dev -> hid ;
413401
414402 switch (cmd ) {
415403 case HIDIOCGRDESCSIZE :
416- if (put_user (dev -> hid -> rsize , (int __user * )arg ))
417- ret = - EFAULT ;
404+ if (put_user (hid -> rsize , (int __user * )arg ))
405+ return - EFAULT ;
418406 break ;
419407
420408 case HIDIOCGRDESC :
421409 {
422410 __u32 len ;
423411
424412 if (get_user (len , (int __user * )arg ))
425- ret = - EFAULT ;
426- else if (len > HID_MAX_DESCRIPTOR_SIZE - 1 )
427- ret = - EINVAL ;
428- else if (copy_to_user (user_arg + offsetof(
429- struct hidraw_report_descriptor ,
430- value [0 ]),
431- dev -> hid -> rdesc ,
432- min (dev -> hid -> rsize , len )))
433- ret = - EFAULT ;
413+ return - EFAULT ;
414+
415+ if (len > HID_MAX_DESCRIPTOR_SIZE - 1 )
416+ return - EINVAL ;
417+
418+ if (copy_to_user (arg + offsetof(
419+ struct hidraw_report_descriptor ,
420+ value [0 ]),
421+ hid -> rdesc ,
422+ min (hid -> rsize , len )))
423+ return - EFAULT ;
424+
434425 break ;
435426 }
436427 case HIDIOCGRAWINFO :
437428 {
438429 struct hidraw_devinfo dinfo ;
439430
440- dinfo .bustype = dev -> hid -> bus ;
441- dinfo .vendor = dev -> hid -> vendor ;
442- dinfo .product = dev -> hid -> product ;
443- if (copy_to_user (user_arg , & dinfo , sizeof (dinfo )))
444- ret = - EFAULT ;
431+ dinfo .bustype = hid -> bus ;
432+ dinfo .vendor = hid -> vendor ;
433+ dinfo .product = hid -> product ;
434+ if (copy_to_user (arg , & dinfo , sizeof (dinfo )))
435+ return - EFAULT ;
445436 break ;
446437 }
447438 case HIDIOCREVOKE :
448439 {
449- if (user_arg )
450- ret = - EINVAL ;
451- else
452- ret = hidraw_revoke (list );
453- break ;
440+ struct hidraw_list * list = file -> private_data ;
441+
442+ if (arg )
443+ return - EINVAL ;
444+
445+ return hidraw_revoke (list );
454446 }
455447 default :
456- {
457- struct hid_device * hid = dev -> hid ;
458- if (_IOC_TYPE (cmd ) != 'H' ) {
459- ret = - EINVAL ;
460- break ;
461- }
448+ /*
449+ * None of the above ioctls can return -EAGAIN, so
450+ * use it as a marker that we need to check variable
451+ * length ioctls.
452+ */
453+ return - EAGAIN ;
454+ }
462455
463- if (_IOC_NR (cmd ) == _IOC_NR (HIDIOCSFEATURE (0 ))) {
464- int len = _IOC_SIZE (cmd );
465- ret = hidraw_send_report (file , user_arg , len , HID_FEATURE_REPORT );
466- break ;
467- }
468- if (_IOC_NR (cmd ) == _IOC_NR (HIDIOCGFEATURE (0 ))) {
469- int len = _IOC_SIZE (cmd );
470- ret = hidraw_get_report (file , user_arg , len , HID_FEATURE_REPORT );
471- break ;
472- }
456+ return 0 ;
457+ }
473458
474- if (_IOC_NR (cmd ) == _IOC_NR (HIDIOCSINPUT (0 ))) {
475- int len = _IOC_SIZE (cmd );
476- ret = hidraw_send_report (file , user_arg , len , HID_INPUT_REPORT );
477- break ;
478- }
479- if (_IOC_NR (cmd ) == _IOC_NR (HIDIOCGINPUT (0 ))) {
480- int len = _IOC_SIZE (cmd );
481- ret = hidraw_get_report (file , user_arg , len , HID_INPUT_REPORT );
482- break ;
483- }
459+ static long hidraw_rw_variable_size_ioctl (struct file * file , struct hidraw * dev , unsigned int cmd ,
460+ void __user * user_arg )
461+ {
462+ int len = _IOC_SIZE (cmd );
463+
464+ switch (cmd & ~IOCSIZE_MASK ) {
465+ case HIDIOCSFEATURE (0 ):
466+ return hidraw_send_report (file , user_arg , len , HID_FEATURE_REPORT );
467+ case HIDIOCGFEATURE (0 ):
468+ return hidraw_get_report (file , user_arg , len , HID_FEATURE_REPORT );
469+ case HIDIOCSINPUT (0 ):
470+ return hidraw_send_report (file , user_arg , len , HID_INPUT_REPORT );
471+ case HIDIOCGINPUT (0 ):
472+ return hidraw_get_report (file , user_arg , len , HID_INPUT_REPORT );
473+ case HIDIOCSOUTPUT (0 ):
474+ return hidraw_send_report (file , user_arg , len , HID_OUTPUT_REPORT );
475+ case HIDIOCGOUTPUT (0 ):
476+ return hidraw_get_report (file , user_arg , len , HID_OUTPUT_REPORT );
477+ }
484478
485- if (_IOC_NR (cmd ) == _IOC_NR (HIDIOCSOUTPUT (0 ))) {
486- int len = _IOC_SIZE (cmd );
487- ret = hidraw_send_report (file , user_arg , len , HID_OUTPUT_REPORT );
488- break ;
489- }
490- if (_IOC_NR (cmd ) == _IOC_NR (HIDIOCGOUTPUT (0 ))) {
491- int len = _IOC_SIZE (cmd );
492- ret = hidraw_get_report (file , user_arg , len , HID_OUTPUT_REPORT );
493- break ;
494- }
479+ return - EINVAL ;
480+ }
495481
496- /* Begin Read-only ioctls. */
497- if (_IOC_DIR (cmd ) != _IOC_READ ) {
498- ret = - EINVAL ;
499- break ;
500- }
482+ static long hidraw_ro_variable_size_ioctl (struct file * file , struct hidraw * dev , unsigned int cmd ,
483+ void __user * user_arg )
484+ {
485+ struct hid_device * hid = dev -> hid ;
486+ int len = _IOC_SIZE (cmd );
487+ int field_len ;
488+
489+ switch (cmd & ~IOCSIZE_MASK ) {
490+ case HIDIOCGRAWNAME (0 ):
491+ field_len = strlen (hid -> name ) + 1 ;
492+ if (len > field_len )
493+ len = field_len ;
494+ return copy_to_user (user_arg , hid -> name , len ) ? - EFAULT : len ;
495+ case HIDIOCGRAWPHYS (0 ):
496+ field_len = strlen (hid -> phys ) + 1 ;
497+ if (len > field_len )
498+ len = field_len ;
499+ return copy_to_user (user_arg , hid -> phys , len ) ? - EFAULT : len ;
500+ case HIDIOCGRAWUNIQ (0 ):
501+ field_len = strlen (hid -> uniq ) + 1 ;
502+ if (len > field_len )
503+ len = field_len ;
504+ return copy_to_user (user_arg , hid -> uniq , len ) ? - EFAULT : len ;
505+ }
501506
502- if (_IOC_NR (cmd ) == _IOC_NR (HIDIOCGRAWNAME (0 ))) {
503- int len = strlen (hid -> name ) + 1 ;
504- if (len > _IOC_SIZE (cmd ))
505- len = _IOC_SIZE (cmd );
506- ret = copy_to_user (user_arg , hid -> name , len ) ?
507- - EFAULT : len ;
508- break ;
509- }
507+ return - EINVAL ;
508+ }
510509
511- if ( _IOC_NR ( cmd ) == _IOC_NR ( HIDIOCGRAWPHYS ( 0 ))) {
512- int len = strlen ( hid -> phys ) + 1 ;
513- if ( len > _IOC_SIZE ( cmd ))
514- len = _IOC_SIZE ( cmd );
515- ret = copy_to_user ( user_arg , hid -> phys , len ) ?
516- - EFAULT : len ;
517- break ;
518- }
510+ static long hidraw_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
511+ {
512+ struct inode * inode = file_inode ( file );
513+ unsigned int minor = iminor ( inode );
514+ struct hidraw * dev ;
515+ struct hidraw_list * list = file -> private_data ;
516+ void __user * user_arg = ( void __user * ) arg ;
517+ int ret ;
519518
520- if (_IOC_NR (cmd ) == _IOC_NR (HIDIOCGRAWUNIQ (0 ))) {
521- int len = strlen (hid -> uniq ) + 1 ;
522- if (len > _IOC_SIZE (cmd ))
523- len = _IOC_SIZE (cmd );
524- ret = copy_to_user (user_arg , hid -> uniq , len ) ?
525- - EFAULT : len ;
526- break ;
527- }
528- }
519+ down_read (& minors_rwsem );
520+ dev = hidraw_table [minor ];
521+ if (!dev || !dev -> exist || hidraw_is_revoked (list )) {
522+ ret = - ENODEV ;
523+ goto out ;
524+ }
525+
526+ if (_IOC_TYPE (cmd ) != 'H' ) {
527+ ret = - EINVAL ;
528+ goto out ;
529+ }
529530
531+ if (_IOC_NR (cmd ) > HIDIOCTL_LAST || _IOC_NR (cmd ) == 0 ) {
530532 ret = - ENOTTY ;
533+ goto out ;
531534 }
535+
536+ ret = hidraw_fixed_size_ioctl (file , dev , cmd , user_arg );
537+ if (ret != - EAGAIN )
538+ goto out ;
539+
540+ switch (_IOC_DIR (cmd )) {
541+ case (_IOC_READ | _IOC_WRITE ):
542+ ret = hidraw_rw_variable_size_ioctl (file , dev , cmd , user_arg );
543+ break ;
544+ case _IOC_READ :
545+ ret = hidraw_ro_variable_size_ioctl (file , dev , cmd , user_arg );
546+ break ;
547+ default :
548+ /* Any other IOC_DIR is wrong */
549+ ret = - EINVAL ;
550+ }
551+
532552out :
533553 up_read (& minors_rwsem );
534554 return ret ;
0 commit comments