Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 19 Oct 2011 15:56:30 +0400
From:      Alexandr Matveev <timon@timon.net.nz>
To:        freebsd-drivers@freebsd.org
Subject:   Writing HID driver
Message-ID:  <4E9EBAEE.2070500@timon.net.nz>

next in thread | raw e-mail | index | archive | help
Hi,

I'm writing the driver for the USB keyboard, which has two interfaces:
first is generic keyboard and second is HID device. If I load driver and
then attach the keyboard - everything is OK. But if I attach keyboard
and then kldload driver, it won't attach to the device because the default
uhid driver already attached to it first. To prevent this, driver 
searches for
uhid devices after being loaded and compares a pnpinfo string to search
for suitable devices and detach them.

Everything works fine, but I have two questions:
1) Is there any simpler way to do the same thing?
2) Is there a way to get device vendor & product without using device
bus-specific functions?

Code which I use now:

DRIVER_MODULE(lkbd, uhub, lkbd_driver, lkbd_devclass, lkbd_modevent, 0);

static int
lkbd_modevent(module_t mod, int what, void *arg)
{
         switch (what) {
         case MOD_LOAD:
                 lkbd_detach_uhid();
                 break;
         case MOD_UNLOAD:
                 break;
         default:
                 return (EOPNOTSUPP);
         }
         return (0);
}

static void
lkbd_detach_uhid(void)
{
         struct u_businfo ubus;
         size_t           ub_size = sizeof(ubus);
         struct u_device  udev;
         size_t           ud_size = sizeof(udev);

         int              name2oid[2];
         int              oid[CTL_MAXNAME];
         size_t           oid_size, plen;
         char             *name;
         int              dev_ptr;
         int              error;

         if (kernel_sysctlbyname(curthread, "hw.bus.info", &ubus, 
&ub_size, NULL, 0, NULL, 0) != 0) {
                 DPRINTFN(3, "Can't get hw.bus.info sysctl node\n");
                 return;
         }

         /* Here is some magic from 
kern/kern_sysctl.c:kernel_sysctlbyname */
         name2oid[0] = 0;
         name2oid[1] = 3;
         oid_size = sizeof(oid);
         name = "hw.bus.devices";
         error = kernel_sysctl(curthread, name2oid, 2, oid, &oid_size, 
name, strlen(name), &plen, 0);
         if (error) {
                 DPRINTFN(3, "Can't find hw.bus.devices sysctl node\n");
                 return;
         }
         plen /= sizeof(int);
         /* End of magic */
         oid[plen++] = ubus.ub_generation;
         dev_ptr = plen++;

         oid[dev_ptr] = 0;
         while (kernel_sysctl(curthread, oid, plen, &udev, &ud_size, 
NULL, 0, NULL, 0) == 0) {
                 if (
                         strncmp(udev.dv_drivername, "uhid", 4) == 0
&& strstr(udev.dv_pnpinfo, "vendor=0x046d product=0xc228") != NULL
&& device_is_attached((device_t)udev.dv_handle)
                 ) {
                         device_detach((device_t)udev.dv_handle);
                 }
                 oid[dev_ptr]++;
         }

         return;
}

-- 
Alexandr Matveev




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4E9EBAEE.2070500>