- Cleaned up usb_printer driver.

- Fixed issues sending data over USB bus (actually write data when
requested to; increased timeout when writing data).
- Added usb_printer to build and Haiku image.
- Sending data to printer over USB bus works now in Haiku (cat ... >
/dev/priner/usb/0). Not sure if it works when printing from an
application as I don't have a driver that supports my printer yet.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33501 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Pfeiffer
2009-10-10 15:08:58 +00:00
parent 56cd7d5c50
commit afa7938a8d
4 changed files with 15 additions and 548 deletions
+1
View File
@@ -228,6 +228,7 @@ AddDriversToHaikuImage midi : $(SYSTEM_ADD_ONS_DRIVERS_MIDI) ;
AddDriversToHaikuImage bus : usb_raw fw_raw ;
AddDriversToHaikuImage disk floppy : $(X86_ONLY)pc_floppy ;
AddDriversToHaikuImage disk usb : usb_disk ;
AddDriversToHaikuImage printer usb : usb_printer ;
AddDriversToHaikuImage disk virtual : nbd ;
AddDriversToHaikuImage dvb : cx23882 ;
AddDriversToHaikuImage graphics : $(SYSTEM_ADD_ONS_DRIVERS_GRAPHICS) ;
+1
View File
@@ -14,6 +14,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers misc ;
SubInclude HAIKU_TOP src add-ons kernel drivers network ;
SubInclude HAIKU_TOP src add-ons kernel drivers ports ;
SubInclude HAIKU_TOP src add-ons kernel drivers power ;
SubInclude HAIKU_TOP src add-ons kernel drivers printer ;
SubInclude HAIKU_TOP src add-ons kernel drivers random ;
SubInclude HAIKU_TOP src add-ons kernel drivers tty ;
SubInclude HAIKU_TOP src add-ons kernel drivers video ;
@@ -20,8 +20,16 @@
#define DEVICE_NAME_BASE "printer/usb/"
#define DEVICE_NAME DEVICE_NAME_BASE"%ld"
#define SECONDS ((bigtime_t)1000 * 1000)
#define MINUTES (60 * SECONDS)
#define HOURS (60 * MINUTES)
#define TRACE_USB_PRINTER 1
// large timeout so user can fix any issues (for instance insert paper)
// before printing can start or continue
#define WRITE_TIMEOUT (1 * HOURS)
#define READ_TIMEOUT (2 * SECONDS)
// #define TRACE_USB_PRINTER 1
#ifdef TRACE_USB_PRINTER
#define TRACE(x...) dprintf(DRIVER_NAME": "x)
#define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME": "x)
@@ -50,25 +58,6 @@ static void usb_printer_callback(void *cookie, status_t status, void *data,
status_t usb_printer_transfer_data(printer_device *device, bool directionIn,
void *data, size_t dataLength);
#if 0
status_t usb_printer_mass_storage_reset(printer_device *device);
uint8 usb_printer_get_max_lun(printer_device *device);
void usb_printer_reset_recovery(printer_device *device);
status_t usb_printer_receive_csw(printer_device *device,
command_status_wrapper *status);
status_t usb_printer_operation(device_lun *lun, uint8 operation,
uint8 opLength, uint32 logicalBlockAddress,
uint16 transferLength, void *data, uint32 *dataLength,
bool directionIn);
status_t usb_printer_request_sense(device_lun *lun);
status_t usb_printer_mode_sense(device_lun *lun);
status_t usb_printer_test_unit_ready(device_lun *lun);
status_t usb_printer_inquiry(device_lun *lun);
status_t usb_printer_reset_capacity(device_lun *lun);
status_t usb_printer_update_capacity(device_lun *lun);
status_t usb_printer_synchronize(device_lun *lun, bool force);
#endif
//
//#pragma mark - Device Allocation Helper Functions
@@ -110,7 +99,8 @@ usb_printer_transfer_data(printer_device *device, bool directionIn, void *data,
}
do {
result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 2000000);
bigtime_t timeout = directionIn ? READ_TIMEOUT : WRITE_TIMEOUT;
result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, timeout);
if (result == B_TIMED_OUT) {
// Cancel the transfer and collect the sem that should now be
// released through the callback on cancel. Handling of device
@@ -130,403 +120,6 @@ usb_printer_transfer_data(printer_device *device, bool directionIn, void *data,
return B_OK;
}
#if 0
status_t
usb_printer_mass_storage_reset(printer_device *device)
{
return gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_OUT
| USB_REQTYPE_CLASS, REQUEST_MASS_STORAGE_RESET, 0x0000,
device->interface, 0, NULL, NULL);
}
uint8
usb_printer_get_max_lun(printer_device *device)
{
uint8 result = 0;
size_t actualLength = 0;
// devices that do not support multiple LUNs may stall this request
if (gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_IN
| USB_REQTYPE_CLASS, REQUEST_GET_MAX_LUN, 0x0000, device->interface,
1, &result, &actualLength) != B_OK || actualLength != 1)
return 0;
if (result > MAX_LOGICAL_UNIT_NUMBER) {
// invalid max lun
return 0;
}
return result;
}
status_t
usb_printer_receive_csw(printer_device *device, command_status_wrapper *status)
{
status_t result = usb_printer_transfer_data(device, true, status,
sizeof(command_status_wrapper));
if (result != B_OK)
return result;
if (device->status != B_OK
|| device->actual_length != sizeof(command_status_wrapper)) {
// receiving the command status wrapper failed
return B_ERROR;
}
return B_OK;
}
status_t
usb_printer_operation(device_lun *lun, uint8 operation, uint8 opLength,
uint32 logicalBlockAddress, uint16 transferLength, void *data,
uint32 *dataLength, bool directionIn)
{
TRACE("operation: lun: %u; op: %u; oplen: %u; lba: %lu; tlen: %u; data: %p; dlen: %p (%lu); in: %c\n",
lun->logical_unit_number, operation, opLength, logicalBlockAddress,
transferLength, data, dataLength, dataLength ? *dataLength : 0,
directionIn ? 'y' : 'n');
printer_device *device = lun->device;
command_block_wrapper command;
command.signature = CBW_SIGNATURE;
command.tag = device->current_tag++;
command.data_transfer_length = (dataLength != NULL ? *dataLength : 0);
command.flags = (directionIn ? CBW_DATA_INPUT : CBW_DATA_OUTPUT);
command.lun = lun->logical_unit_number;
command.command_block_length = opLength;
memset(command.command_block, 0, sizeof(command.command_block));
switch (opLength) {
case 6: {
scsi_command_6 *commandBlock = (scsi_command_6 *)command.command_block;
commandBlock->operation = operation;
commandBlock->lun = lun->logical_unit_number << 5;
commandBlock->allocation_length = (uint8)transferLength;
if (operation == SCSI_MODE_SENSE_6) {
// we hijack the lba argument to transport the desired page
commandBlock->reserved[1] = (uint8)logicalBlockAddress;
}
break;
}
case 10: {
scsi_command_10 *commandBlock = (scsi_command_10 *)command.command_block;
commandBlock->operation = operation;
commandBlock->lun_flags = lun->logical_unit_number << 5;
commandBlock->logical_block_address = htonl(logicalBlockAddress);
commandBlock->transfer_length = htons(transferLength);
break;
}
default:
TRACE_ALWAYS("unsupported operation length %d\n", opLength);
return B_BAD_VALUE;
}
status_t result = usb_printer_transfer_data(device, false, &command,
sizeof(command_block_wrapper));
if (result != B_OK)
return result;
if (device->status != B_OK ||
device->actual_length != sizeof(command_block_wrapper)) {
// sending the command block wrapper failed
TRACE_ALWAYS("sending the command block wrapper failed\n");
usb_printer_reset_recovery(device);
return B_ERROR;
}
size_t transferedData = 0;
if (data != NULL && dataLength != NULL && *dataLength > 0) {
// we have data to transfer in a data stage
result = usb_printer_transfer_data(device, directionIn, data, *dataLength);
if (result != B_OK)
return result;
transferedData = device->actual_length;
if (device->status != B_OK || transferedData != *dataLength) {
// sending or receiving of the data failed
if (device->status == B_DEV_STALLED) {
TRACE("stall while transfering data\n");
gUSBModule->clear_feature(directionIn ? device->bulk_in
: device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
} else {
TRACE_ALWAYS("sending or receiving of the data failed\n");
usb_printer_reset_recovery(device);
return B_ERROR;
}
}
}
command_status_wrapper status;
result = usb_printer_receive_csw(device, &status);
if (result != B_OK) {
// in case of a stall or error clear the stall and try again
gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
result = usb_printer_receive_csw(device, &status);
}
if (result != B_OK) {
TRACE_ALWAYS("receiving the command status wrapper failed\n");
usb_printer_reset_recovery(device);
return result;
}
if (status.signature != CSW_SIGNATURE || status.tag != command.tag) {
// the command status wrapper is not valid
TRACE_ALWAYS("command status wrapper is not valid\n");
usb_printer_reset_recovery(device);
return B_ERROR;
}
switch (status.status) {
case CSW_STATUS_COMMAND_PASSED:
case CSW_STATUS_COMMAND_FAILED: {
if (status.data_residue > command.data_transfer_length) {
// command status wrapper is not meaningful
TRACE_ALWAYS("command status wrapper has invalid residue\n");
usb_printer_reset_recovery(device);
return B_ERROR;
}
if (dataLength != NULL) {
*dataLength -= status.data_residue;
if (transferedData < *dataLength) {
TRACE_ALWAYS("less data transfered than indicated\n");
*dataLength = transferedData;
}
}
if (status.status == CSW_STATUS_COMMAND_PASSED) {
// the operation is complete and has succeeded
return B_OK;
} else {
// the operation is complete but has failed at the SCSI level
TRACE_ALWAYS("operation 0x%02x failed at the SCSI level\n",
operation);
result = usb_printer_request_sense(lun);
return result == B_OK ? B_ERROR : result;
}
}
case CSW_STATUS_PHASE_ERROR: {
// a protocol or device error occured
TRACE_ALWAYS("phase error in operation 0x%02x\n", operation);
usb_printer_reset_recovery(device);
return B_ERROR;
}
default: {
// command status wrapper is not meaningful
TRACE_ALWAYS("command status wrapper has invalid status\n");
usb_printer_reset_recovery(device);
return B_ERROR;
}
}
}
//
//#pragma mark - Helper/Convenience Functions
//
status_t
usb_printer_request_sense(device_lun *lun)
{
uint32 dataLength = sizeof(scsi_request_sense_6_parameter);
scsi_request_sense_6_parameter parameter;
status_t result = usb_printer_operation(lun, SCSI_REQUEST_SENSE_6, 6, 0,
dataLength, &parameter, &dataLength, true);
if (result != B_OK) {
TRACE_ALWAYS("getting request sense data failed\n");
return result;
}
if (parameter.sense_key > SCSI_SENSE_KEY_NOT_READY) {
TRACE_ALWAYS("request_sense: key: 0x%02x; asc: 0x%02x; ascq: 0x%02x;\n",
parameter.sense_key, parameter.additional_sense_code,
parameter.additional_sense_code_qualifier);
}
switch (parameter.sense_key) {
case SCSI_SENSE_KEY_NO_SENSE:
case SCSI_SENSE_KEY_RECOVERED_ERROR:
return B_OK;
case SCSI_SENSE_KEY_NOT_READY:
TRACE("request_sense: device not ready (asc 0x%02x ascq 0x%02x)\n",
parameter.additional_sense_code,
parameter.additional_sense_code_qualifier);
lun->media_present = false;
usb_printer_reset_capacity(lun);
return B_DEV_NO_MEDIA;
case SCSI_SENSE_KEY_HARDWARE_ERROR:
case SCSI_SENSE_KEY_MEDIUM_ERROR:
TRACE_ALWAYS("request_sense: media or hardware error\n");
return B_DEV_UNREADABLE;
case SCSI_SENSE_KEY_ILLEGAL_REQUEST:
TRACE_ALWAYS("request_sense: illegal request\n");
return B_DEV_INVALID_IOCTL;
case SCSI_SENSE_KEY_UNIT_ATTENTION:
TRACE_ALWAYS("request_sense: media changed\n");
lun->media_changed = true;
lun->media_present = true;
return B_DEV_MEDIA_CHANGED;
case SCSI_SENSE_KEY_DATA_PROTECT:
TRACE_ALWAYS("request_sense: write protected\n");
return B_READ_ONLY_DEVICE;
case SCSI_SENSE_KEY_ABORTED_COMMAND:
TRACE_ALWAYS("request_sense: command aborted\n");
return B_CANCELED;
}
return B_ERROR;
}
status_t
usb_printer_mode_sense(device_lun *lun)
{
uint32 dataLength = sizeof(scsi_mode_sense_6_parameter);
scsi_mode_sense_6_parameter parameter;
status_t result = usb_printer_operation(lun, SCSI_MODE_SENSE_6, 6,
SCSI_MODE_PAGE_DEVICE_CONFIGURATION, dataLength, &parameter,
&dataLength, true);
if (result != B_OK) {
TRACE_ALWAYS("getting mode sense data failed\n");
return result;
}
lun->write_protected
= (parameter.device_specific & SCSI_DEVICE_SPECIFIC_WRITE_PROTECT) != 0;
TRACE_ALWAYS("write protected: %s\n", lun->write_protected ? "yes" : "no");
return B_OK;
}
status_t
usb_printer_test_unit_ready(device_lun *lun)
{
return usb_printer_operation(lun, SCSI_TEST_UNIT_READY_6, 6, 0, 0, NULL, NULL,
true);
}
status_t
usb_printer_inquiry(device_lun *lun)
{
uint32 dataLength = sizeof(scsi_inquiry_6_parameter);
scsi_inquiry_6_parameter parameter;
status_t result = B_ERROR;
for (uint32 tries = 0; tries < 3; tries++) {
result = usb_printer_operation(lun, SCSI_INQUIRY_6, 6, 0, dataLength,
&parameter, &dataLength, true);
if (result == B_OK)
break;
}
if (result != B_OK) {
TRACE_ALWAYS("getting inquiry data failed\n");
lun->device_type = B_DISK;
lun->removable = true;
return result;
}
TRACE("peripherial_device_type 0x%02x\n", parameter.peripherial_device_type);
TRACE("peripherial_qualifier 0x%02x\n", parameter.peripherial_qualifier);
TRACE("removable_medium %s\n", parameter.removable_medium ? "yes" : "no");
TRACE("version 0x%02x\n", parameter.version);
TRACE("response_data_format 0x%02x\n", parameter.response_data_format);
TRACE_ALWAYS("vendor_identification \"%.8s\"\n", parameter.vendor_identification);
TRACE_ALWAYS("product_identification \"%.16s\"\n", parameter.product_identification);
TRACE_ALWAYS("product_revision_level \"%.4s\"\n", parameter.product_revision_level);
lun->device_type = parameter.peripherial_device_type; /* 1:1 mapping */
lun->removable = (parameter.removable_medium == 1);
return B_OK;
}
status_t
usb_printer_reset_capacity(device_lun *lun)
{
lun->block_size = 512;
lun->block_count = 0;
return B_OK;
}
status_t
usb_printer_update_capacity(device_lun *lun)
{
uint32 dataLength = sizeof(scsi_read_capacity_10_parameter);
scsi_read_capacity_10_parameter parameter;
status_t result = B_ERROR;
// Retry reading the capacity up to three times. The first try might only
// yield a unit attention telling us that the device or media status
// changed, which is more or less expected if it is the first operation
// on the device or the device only clears the unit atention for capacity
// reads.
for (int32 i = 0; i < 3; i++) {
result = usb_printer_operation(lun, SCSI_READ_CAPACITY_10, 10, 0, 0,
&parameter, &dataLength, true);
if (result == B_OK)
break;
}
if (result != B_OK) {
TRACE_ALWAYS("failed to update capacity\n");
lun->media_present = false;
lun->media_changed = false;
usb_printer_reset_capacity(lun);
return result;
}
lun->media_present = true;
lun->media_changed = false;
lun->block_size = ntohl(parameter.logical_block_length);
lun->block_count = ntohl(parameter.last_logical_block_address) + 1;
return B_OK;
}
status_t
usb_printer_synchronize(device_lun *lun, bool force)
{
if (lun->device->sync_support == 0) {
// this device reported an illegal request when syncing or repeatedly
// returned an other error, it apparently does not support syncing...
return B_UNSUPPORTED;
}
if (!lun->should_sync && !force)
return B_OK;
status_t result = usb_printer_operation(lun, SCSI_SYNCHRONIZE_CACHE_10, 10,
0, 0, NULL, NULL, false);
if (result == B_OK) {
lun->device->sync_support = SYNC_SUPPORT_RELOAD;
lun->should_sync = false;
return B_OK;
}
if (result == B_DEV_INVALID_IOCTL)
lun->device->sync_support = 0;
else
lun->device->sync_support--;
return result;
}
#endif
//
//#pragma mark - Device Attach/Detach Notifications and Callback
@@ -537,7 +130,6 @@ static void
usb_printer_callback(void *cookie, status_t status, void *data,
size_t actualLength)
{
//TRACE("callback()\n");
printer_device *device = (printer_device *)cookie;
device->status = status;
device->actual_length = actualLength;
@@ -555,7 +147,6 @@ usb_printer_device_added(usb_device newDevice, void **cookie)
device->open_count = 0;
device->interface = 0xff;
device->current_tag = 0;
device->sync_support = SYNC_SUPPORT_RELOAD;
device->block_size = 4096;
// scan through the interfaces to find our bulk-only data interface
@@ -692,78 +283,6 @@ usb_printer_device_removed(void *cookie)
}
//
//#pragma mark - Partial Buffer Functions
//
#if 0
static bool
usb_printer_needs_partial_buffer(printer_device *device, off_t position, size_t length,
uint32 &blockPosition, uint16 &blockCount)
{
blockPosition = (uint32)(position / device->block_size);
if ((off_t)blockPosition * device->block_size != position)
return true;
blockCount = (uint16)(length / device->block_size);
if ((size_t)blockCount * device->block_size != length)
return true;
return false;
}
static status_t
usb_printer_block_read(printer_device *device, uint32 blockPosition, uint16 blockCount,
void *buffer, size_t *length)
{
status_t result = usb_printer_operation(device, SCSI_READ_10, 10, blockPosition,
blockCount, buffer, length, true);
return result;
}
static status_t
usb_printer_block_write(printer_device *device, uint32 blockPosition, uint16 blockCount,
void *buffer, size_t *length)
{
status_t result = usb_printer_operation(device, SCSI_WRITE_10, 10, blockPosition,
blockCount, buffer, length, false);
if (result == B_OK)
lun->should_sync = true;
return result;
}
static status_t
usb_printer_prepare_partial_buffer(printer_device *device, off_t position, size_t length,
void *&partialBuffer, void *&blockBuffer, uint32 &blockPosition,
uint16 &blockCount)
{
blockPosition = (uint32)(position / lun->block_size);
blockCount = (uint16)((uint32)((position + length + lun->block_size - 1)
/ lun->block_size) - blockPosition);
size_t blockLength = blockCount * lun->block_size;
blockBuffer = malloc(blockLength);
if (blockBuffer == NULL) {
TRACE_ALWAYS("no memory to allocate partial buffer\n");
return B_NO_MEMORY;
}
status_t result = usb_printer_block_read(device, blockPosition, blockCount,
blockBuffer, &blockLength);
if (result != B_OK) {
TRACE_ALWAYS("block read failed when filling partial buffer\n");
free(blockBuffer);
return result;
}
off_t offset = position - (blockPosition * lun->block_size);
partialBuffer = (uint8 *)blockBuffer + offset;
return B_OK;
}
#endif
//
//#pragma mark - Driver Hooks
//
@@ -840,7 +359,7 @@ usb_printer_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
switch (op) {
case USB_PRINTER_GET_DEVICE_ID: {
// TODO implement
strcpy(buffer, "Not implemented");
strncpy((char*)buffer, "Not implemented", length);
result = B_OK;
break;
}
@@ -863,7 +382,7 @@ usb_printer_transfer(printer_device* device, bool directionIn, void* buffer, siz
return result;
}
result = usb_printer_transfer_data(device, true, buffer, *length);
result = usb_printer_transfer_data(device, directionIn, buffer, *length);
if (result != B_OK) {
return result;
}
@@ -31,21 +31,6 @@
#define REQUEST_GET_PORT_STATUS 0x01
#define REQUEST_SOFT_RESET 0x02
#if 0
#define CBW_SIGNATURE 0x43425355
#define CBW_DATA_OUTPUT 0x00
#define CBW_DATA_INPUT 0x80
#define CSW_SIGNATURE 0x53425355
#define CSW_STATUS_COMMAND_PASSED 0x00
#define CSW_STATUS_COMMAND_FAILED 0x01
#define CSW_STATUS_PHASE_ERROR 0x02
#endif
// TODO remove?
#define SYNC_SUPPORT_RELOAD 5
typedef struct printer_device_s {
usb_device device;
uint32 device_number;
@@ -60,7 +45,6 @@ typedef struct printer_device_s {
usb_pipe bulk_out;
uint8 interface;
uint32 current_tag;
uint8 sync_support;
// used to store callback information
sem_id notify;
@@ -71,42 +55,4 @@ typedef struct printer_device_s {
uint32 block_size;
} printer_device;
#if 0
// represents a logical unit on the pointed to device - this gets published
struct device_lun_s {
disk_device *device;
char name[32];
uint8 logical_unit_number;
bool should_sync;
// device information through read capacity/inquiry
bool media_present;
bool media_changed;
uint32 block_count;
uint32 block_size;
uint8 device_type;
bool removable;
bool write_protected;
};
typedef struct command_block_wrapper_s {
uint32 signature;
uint32 tag;
uint32 data_transfer_length;
uint8 flags;
uint8 lun;
uint8 command_block_length;
uint8 command_block[16];
} _PACKED command_block_wrapper;
typedef struct command_status_wrapper_s {
uint32 signature;
uint32 tag;
uint32 data_residue;
uint8 status;
} _PACKED command_status_wrapper;
#endif
#endif // _USB_PRINTER_H_