mmlr+stippi+axeld:
* changed the way the interrupt schedules and repeats are handled, the input server thread (via ioctl) is now triggering the scheduling and processing of usb interrupt transfers. As long as it is blocking on the usb_callback semaphore, it is using it as timeout for the key repeats. -> no more deadlock because the driver is issuing usb commands from within the usb_callback function (hotplugging should be fixed when using the Haiku usb stack) -> the driver is no longer installing one timer per key down event -> simplifications git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23328 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
d688a5ad65
commit
e7447cc4bb
@ -339,7 +339,7 @@ create_device(const usb_device *dev, const usb_interface_info *ii,
|
||||
device->open = 0;
|
||||
device->open_fds = NULL;
|
||||
device->active = true;
|
||||
device->unplugged = false;
|
||||
device->transfer_scheduled = false;
|
||||
device->insns = NULL;
|
||||
device->num_insns = 0;
|
||||
device->flags = 0;
|
||||
@ -355,7 +355,6 @@ create_device(const usb_device *dev, const usb_interface_info *ii,
|
||||
// default values taken from the PS/2 driver
|
||||
device->repeat_rate = 35000;
|
||||
device->repeat_delay = 300000;
|
||||
device->repeat_timer.device = device;
|
||||
|
||||
return device;
|
||||
}
|
||||
@ -365,7 +364,6 @@ void
|
||||
remove_device(hid_device_info *device)
|
||||
{
|
||||
assert(device != NULL);
|
||||
cancel_timer(&device->repeat_timer.timer);
|
||||
|
||||
if (device->rbuf != NULL) {
|
||||
delete_ring_buffer(device->rbuf);
|
||||
@ -388,28 +386,6 @@ write_key(hid_device_info *device, uint32 key, bool down)
|
||||
raw.timestamp = system_time();
|
||||
|
||||
ring_buffer_write(device->rbuf, (const uint8*)&raw, sizeof(raw_key_info));
|
||||
release_sem_etc(device->sem_cb, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
timer_repeat_hook(struct timer *timer)
|
||||
{
|
||||
hid_device_info *device = ((struct hid_repeat_timer *)timer)->device;
|
||||
|
||||
write_key(device, device->repeat_timer.key, true);
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
timer_delay_hook(struct timer *timer)
|
||||
{
|
||||
hid_device_info *device = ((struct hid_repeat_timer *)timer)->device;
|
||||
|
||||
add_timer(&device->repeat_timer.timer, timer_repeat_hook, device->repeat_rate,
|
||||
B_PERIODIC_TIMER);
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
|
||||
@ -457,10 +433,8 @@ interpret_kb_buffer(hid_device_info *device)
|
||||
write_key(device, key, true);
|
||||
|
||||
// repeat handling
|
||||
cancel_timer(&device->repeat_timer.timer);
|
||||
device->repeat_timer.key = key;
|
||||
add_timer(&device->repeat_timer.timer, timer_delay_hook,
|
||||
device->repeat_delay, B_ONE_SHOT_RELATIVE_TIMER);
|
||||
device->repeat_timer.current_delay = device->repeat_delay;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
@ -492,8 +466,10 @@ interpret_kb_buffer(hid_device_info *device)
|
||||
}
|
||||
|
||||
write_key(device, key, false);
|
||||
|
||||
// cancel the repeats if they are for this key
|
||||
if (device->repeat_timer.key == key)
|
||||
cancel_timer(&device->repeat_timer.timer);
|
||||
device->repeat_timer.current_delay = B_INFINITE_TIMEOUT;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
@ -590,9 +566,10 @@ interpret_mouse_buffer(hid_device_info *device)
|
||||
|
||||
info.timestamp = device->timestamp;
|
||||
ring_buffer_write(device->rbuf, (const uint8*)&info, sizeof(info));
|
||||
release_sem_etc(device->sem_cb, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
// #pragma mark - interrupt transfers
|
||||
|
||||
|
||||
/*!
|
||||
callback: got a report, issue next request
|
||||
@ -602,21 +579,39 @@ usb_callback(void *cookie, status_t busStatus,
|
||||
void *data, size_t actualLength)
|
||||
{
|
||||
hid_device_info *device = cookie;
|
||||
status_t status;
|
||||
|
||||
if (device == NULL || device->unplugged)
|
||||
return;
|
||||
|
||||
acquire_sem(device->sem_lock);
|
||||
device->actual_length = actualLength;
|
||||
device->bus_status = busStatus; /* B_USB_STATUS_* */
|
||||
if (busStatus != B_OK) {
|
||||
|
||||
// release the notification semaphore so the input_server
|
||||
// thread which is blocking in hid_device_control can continue
|
||||
// to run (see you in handle_interrupt_transfer())
|
||||
release_sem_etc(device->sem_cb, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
static status_t
|
||||
schedule_interrupt_transfer(hid_device_info* device)
|
||||
{
|
||||
status_t status = usb->queue_interrupt(device->ept->handle, device->buffer,
|
||||
device->total_report_size, usb_callback, device);
|
||||
if (status != B_OK) {
|
||||
/* XXX probably endpoint stall */
|
||||
DPRINTF_ERR ((MY_ID "queue_interrupt() error %d\n", (int)status));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static status_t
|
||||
handle_interrupt_transfer(hid_device_info* device)
|
||||
{
|
||||
status_t status = device->bus_status;
|
||||
|
||||
if (status != B_OK) {
|
||||
/* request failed */
|
||||
release_sem(device->sem_lock);
|
||||
DPRINTF_ERR((MY_ID "bus status %d\n", (int)busStatus));
|
||||
if (busStatus == B_CANCELED) {
|
||||
DPRINTF_ERR((MY_ID "bus status %d\n", (int)device->bus_status));
|
||||
if (status == B_CANCELED) {
|
||||
/* cancelled: device is unplugged */
|
||||
return;
|
||||
return status;
|
||||
}
|
||||
#if 1
|
||||
status = usb->clear_feature(device->ept->handle, USB_FEATURE_ENDPOINT_HALT);
|
||||
@ -641,18 +636,9 @@ usb_callback(void *cookie, status_t busStatus,
|
||||
memcpy(device->last_buffer, device->buffer, device->total_report_size);
|
||||
} else
|
||||
interpret_mouse_buffer(device);
|
||||
|
||||
release_sem(device->sem_lock);
|
||||
}
|
||||
|
||||
/* issue next request */
|
||||
|
||||
status = usb->queue_interrupt(device->ept->handle, device->buffer,
|
||||
device->total_report_size, usb_callback, device);
|
||||
if (status != B_OK) {
|
||||
/* XXX probably endpoint stall */
|
||||
DPRINTF_ERR ((MY_ID "queue_interrupt() error %d\n", (int)status));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@ -841,15 +827,7 @@ hid_device_added(const usb_device *dev, void **cookie)
|
||||
|
||||
DPRINTF_INFO ((MY_ID "%08lx %08lx %08lx\n", *(((uint32*)device->buffer)), *(((uint32*)device->buffer)+1), *(((uint32*)device->buffer)+2)));
|
||||
|
||||
/* issue interrupt transfer */
|
||||
|
||||
device->ept = &intf->endpoint[0]; /* interrupt IN */
|
||||
status = usb->queue_interrupt(device->ept->handle, device->buffer,
|
||||
device->total_report_size, usb_callback, device);
|
||||
if (status != B_OK) {
|
||||
DPRINTF_ERR ((MY_ID "queue_interrupt() error %d\n", (int)status));
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
/* create a port */
|
||||
|
||||
@ -870,7 +848,6 @@ hid_device_removed(void *cookie)
|
||||
|
||||
DPRINTF_INFO((MY_ID "device_removed(%s)\n", device->name));
|
||||
|
||||
device->unplugged = true;
|
||||
usb->cancel_queued_transfers (device->ept->handle);
|
||||
remove_device_info(device);
|
||||
|
||||
@ -950,14 +927,43 @@ hid_device_control(driver_cookie *cookie, uint32 op,
|
||||
if (device->is_keyboard)
|
||||
switch (op) {
|
||||
case KB_READ:
|
||||
err = acquire_sem_etc(device->sem_cb, 1, B_CAN_INTERRUPT, 0LL);
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
acquire_sem(device->sem_lock);
|
||||
while (ring_buffer_readable(device->rbuf) == 0) {
|
||||
if (!device->transfer_scheduled) {
|
||||
err = schedule_interrupt_transfer(device);
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
device->transfer_scheduled = true;
|
||||
// NOTE: this thread is now blocking until
|
||||
// the semaphore will be released from the
|
||||
// call_back function
|
||||
}
|
||||
|
||||
err = acquire_sem_etc(device->sem_cb, 1,
|
||||
B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,
|
||||
device->repeat_timer.current_delay);
|
||||
if (err == B_TIMED_OUT) {
|
||||
// this case is for handling key repeats, it means
|
||||
// no interrupt transfer has happened
|
||||
write_key(device, device->repeat_timer.key, true);
|
||||
// the next timeout is reduced to the repeat_rate
|
||||
device->repeat_timer.current_delay = device->repeat_rate;
|
||||
} else if (err == B_OK) {
|
||||
// this case is for when an actual interrupt transfer
|
||||
// happened, it is the only possible reason to be here
|
||||
device->transfer_scheduled = false;
|
||||
err = handle_interrupt_transfer(device);
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// process what is in the ring_buffer, it could be written
|
||||
// there because we handled an interrupt transfer or because
|
||||
// we wrote the current repeat key
|
||||
ring_buffer_user_read(device->rbuf, arg, sizeof(raw_key_info));
|
||||
release_sem(device->sem_lock);
|
||||
return err;
|
||||
break;
|
||||
return B_OK;
|
||||
|
||||
case KB_SET_LEDS:
|
||||
set_leds(device, (uint8 *)arg);
|
||||
@ -966,9 +972,21 @@ hid_device_control(driver_cookie *cookie, uint32 op,
|
||||
else
|
||||
switch (op) {
|
||||
case MS_READ:
|
||||
err = schedule_interrupt_transfer(device);
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
// NOTE: this thread is now blocking until
|
||||
// the semaphore will be released from the
|
||||
// call_back function
|
||||
|
||||
err = acquire_sem_etc(device->sem_cb, 1, B_CAN_INTERRUPT, 0LL);
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
err = handle_interrupt_transfer(device);
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
acquire_sem(device->sem_lock);
|
||||
ring_buffer_user_read(device->rbuf, arg, sizeof(mouse_movement));
|
||||
release_sem(device->sem_lock);
|
||||
|
@ -45,8 +45,7 @@
|
||||
struct driver_cookie;
|
||||
|
||||
struct hid_repeat_timer {
|
||||
struct timer timer;
|
||||
struct hid_device_info *device;
|
||||
bigtime_t current_delay;
|
||||
uint32 key;
|
||||
};
|
||||
|
||||
@ -68,7 +67,6 @@ typedef struct hid_device_info {
|
||||
struct ring_buffer *rbuf;
|
||||
|
||||
bool active;
|
||||
bool unplugged;
|
||||
int open;
|
||||
struct driver_cookie *open_fds;
|
||||
|
||||
@ -76,6 +74,7 @@ typedef struct hid_device_info {
|
||||
int usbd_status, bus_status, cmd_status;
|
||||
int actual_length;
|
||||
const usb_endpoint_info *ept;
|
||||
bool transfer_scheduled;
|
||||
|
||||
report_insn *insns;
|
||||
size_t num_insns;
|
||||
|
Loading…
x
Reference in New Issue
Block a user